October 21, 2015 Re: Kinds of containers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On 10/21/2015 10:21 AM, Timon Gehr wrote:
>>
>
> I still think those should be mutable by default in order to have
> painless interchangeability with other value type containers. Why should
> corresponding ephemeral and persistent containers have different
> interfaces?
>
> I assume you envision code using those to look as follows?
>
> FunSet!int a;
> a=a.with_(1);
> auto b=a;
> a=a.with_(2);
> a=a.with_(3);
> // b = {1}, a={1,2,3};
>
> I think this should be allowed too:
>
> FunSet!int a;
> a.insert(1);
> auto b=a;
> a.insert(2);
> a.insert(3);
> // b = {1}, a={1,2,3};
>
> One of the two versions should be automatically implemented via UFCS.
I recall you and I discussed this briefly in the past, but forgot the conclusion.
So are you saying that a.insert(1) is really rebinding a to be its former value plus a new slot? I.e a.insert(1) is the same as a = a.with_(1)? That would work, but only if former reads of a refer to the old data. E.g.:
FunSet!int a;
auto b = a;
assert(a.empty && b.empty);
a.insert(1);
assert(!a.empty && b.empty);
Right?
Andrei
|
October 21, 2015 Re: Kinds of containers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Wednesday, 21 October 2015 at 14:20:23 UTC, Andrei Alexandrescu wrote:
> Containers will obey subsets of a nomenclature of primitives.
Just to be crystal clear, something like this?
void fun(Container)(ref Container c) if (hasAppend!Container) {
// append stuff to c
}
|
October 21, 2015 Re: Kinds of containers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On 10/21/2015 04:36 PM, Andrei Alexandrescu wrote: > On 10/21/2015 10:21 AM, Timon Gehr wrote: >>> >> >> I still think those should be mutable by default in order to have >> painless interchangeability with other value type containers. Why should >> corresponding ephemeral and persistent containers have different >> interfaces? >> >> I assume you envision code using those to look as follows? >> >> FunSet!int a; >> a=a.with_(1); >> auto b=a; >> a=a.with_(2); >> a=a.with_(3); >> // b = {1}, a={1,2,3}; >> >> I think this should be allowed too: >> >> FunSet!int a; >> a.insert(1); >> auto b=a; >> a.insert(2); >> a.insert(3); >> // b = {1}, a={1,2,3}; >> >> One of the two versions should be automatically implemented via UFCS. > > I recall you and I discussed this briefly in the past, but forgot the > conclusion. > ... IIRC the conclusion was postponed. :-) > So are you saying that a.insert(1) is really rebinding a to be its > former value plus a new slot? I.e a.insert(1) is the same as a = > a.with_(1)? That would work, but only if former reads of a refer to the > old data. E.g.: > > FunSet!int a; > auto b = a; > assert(a.empty && b.empty); > a.insert(1); > assert(!a.empty && b.empty); > > Right? > > > Andrei Exactly. There would be no mutable aliasing. This way, the persistent data type can behave like a mutable value type, such as a COW or eager copy variant, but with nicer/different performance characteristics. It would be great if exchanging different implementation strategies for value semantics will be as seamless as possible. (If the initially chosen strategy turns out to be wrong, this makes the fix easy.) Of course, the implementation of the persistent data structure will be in terms of non-mutating and possibly O(1)-COW operations only. |
October 21, 2015 Re: Kinds of containers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On 10/21/2015 05:08 PM, Timon Gehr wrote:
> There would be no mutable aliasing.
Here, I mean within the data structure itself. There is nothing wrong with:
class Cell{ int x=0; }
FunSet!Cell a;
a.insert(new Cell());
auto b=a;
foreach(c;a) c.x=1;
assert(b.x==1);
This is analogous to:
struct SingletonFunSet(T){
T element;
}
auto a=SingletonFunSet!Cell(new Cell());
auto b=a;
a.element.x=1;
assert(b.x==1);
Here, SingletonFunSet!Cell is a value type, but it's constituents might not be.
|
October 21, 2015 Re: Kinds of containers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On Wednesday, 21 October 2015 at 14:06:43 UTC, Jonathan M Davis wrote:
>> 1. Functional containers.
>
> I fully expect that these have their place, but I honestly have no idea what they would be. When I've used functional languages, I've only ever found these attributes to be a royal pain to deal with, not a benefit. From what I've seen, containers are the sort of thing that almost always always need to be mutable to be of any real benefit. Upon occasion, constructing a container up front and then never tweaking it after that is useful, but that's pretty rare from what I've seen.
>
> The only cases that I can think of where this could be really beneficial would be something like strings, and we're using arrays for those currently (though they are arguably functional containers given that they have immutable elements).
I've found immutable containers useful when working with configuration files. There are only a few places in a program where you want to actively change values in a configuration files (configure the values). For these instances it's useful for the GUI or whatever to grab a mutable container to work with. For all other areas, immutable containers are very helpful in ensuring nobody accidentally modifies something they shouldn't be outside of the provided sandboxes.
|
October 21, 2015 Re: Kinds of containers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ice Cream Man | On Wednesday, 21 October 2015 at 15:18:08 UTC, Ice Cream Man wrote:
> On Wednesday, 21 October 2015 at 14:06:43 UTC, Jonathan M Davis wrote:
>> [...]
>
> I've found immutable containers useful when working with configuration files. There are only a few places in a program where you want to actively change values in a configuration files (configure the values). For these instances it's useful for the GUI or whatever to grab a mutable container to work with. For all other areas, immutable containers are very helpful in ensuring nobody accidentally modifies something they shouldn't be outside of the provided sandboxes.
Actually, I think functional containers are probably more often than not the right tool for whatever it is you are doing. They just aren't convenient, because you might have to rethinking the architecture.
|
October 21, 2015 Re: Kinds of containers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis Attachments:
| On Wed, 2015-10-21 at 14:06 +0000, Jonathan M Davis via Digitalmars-d wrote: > […] > > 1. Functional containers. > > I fully expect that these have their place, but I honestly have no idea what they would be. When I've used functional languages, I've only ever found these attributes to be a royal pain to deal with, not a benefit. From what I've seen, containers are the sort of thing that almost always always need to be mutable to be of any real benefit. Upon occasion, constructing a container up front and then never tweaking it after that is useful, but that's pretty rare from what I've seen. > > The only cases that I can think of where this could be really beneficial would be something like strings, and we're using arrays for those currently (though they are arguably functional containers given that they have immutable elements). I am confused as to why you would find these functional/persistent data structures to be a problem in functional languages. The whole point of them is they can be easily shared without any locks. In effect each amendment of the structure creates a fork (effectively a copy but without the copy bit) of it. This goes hand in hand with the functional, and especially tail recursive computational model. The Groovy team is currently debating adding persistent data structures to the Groovy armoury. Clojure and Scala have had persistent data structures for ages. The Clojure ones are really good. The Scala ones have some issues so there is currently discussion of a third rewrite. > > 2. Reference containers. > > These are where it's at IMHO. 99.999999% this is what makes sense. Except in a concurrent and parallel world! For big mutable data structures you end up creating agents (or so we can use hyper trendy nomenclature, pico-services) to avoid messing around with locks, mutexes, etc. the use of which generally kills performance. > > 3. Eager value containers. > > […] As noted in an earlier email, I am not yes convinced by this one. > > 4. Copy-on-write containers. > > This could be interesting for some applications (probably more so than functional containers), but I would expect that it would only really be useful for very specific containers. I certainly wouldn't want to end up with a copy of my vector or list because I added a value to it. COW for strings makes a lot of sense, because they don't tend to get mutated much (which is also why string is immutable(char)[]). But since we're already using arrays for strings, a COW string would then be for special cases only. I'd say the opposite. I would suggest that persistent/functional data structures would be more generally useful than COW ones. > Overall, I think that we should focus on getting good reference containers while leaving the door open for cool stuff from the other container types. It's the reference containers that most programs are going to need, whereas I expect that the others are more likely to be nice-to-haves for a minority of applications and outright useless for the majority. I am not convinced by this argument. Yes these are the type of containers C++/D/… programmers are used to and have a lot of code using, but are they really the ones that should be used. Experience from Clojure and Scala is that persistent/functional data structures lead to much nicer/easier concurrent and parallel code. -- Russel. ============================================================================= Dr Russel Winder t:+44 20 7585 2200 voip:sip: russel.winder@ekiga.net 41 Buckmaster Road m:+44 7770 465 077 xmpp:russel@winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype:russel_winder |
October 21, 2015 Re: Pandas [was Kinds of containers] | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu Attachments:
| On Wed, 2015-10-21 at 09:11 -0400, Andrei Alexandrescu via Digitalmars-d wrote: > […] > My finance folks also rave about Pandas. I wish I could fork myself to look into it. Pandas is what makes Python a viable competitor to R. Most data science people will use one or the other these days. Though some (with budgets) will use Matlab or Mathematica. > Nevertheless, what I'm working on now should help libraries such as Pandas for D. We have a large language but are unclear on recipe-style idioms for writing library code. > Can I suggest that we avoid "should" and ensure that we have more of a "will". Instead of a bottom up approach, we perhaps need a top-down approach: Applications programmers do X, Y, Z, that are best support by data structures A, B, C, and A, B, and C are realized by library architecture α, β, γ. -- Russel. ============================================================================= Dr Russel Winder t:+44 20 7585 2200 voip:sip: russel.winder@ekiga.net 41 Buckmaster Road m:+44 7770 465 077 xmpp:russel@winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype:russel_winder |
October 21, 2015 Re: Kinds of containers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On 10/21/2015 01:05 PM, Andrei Alexandrescu wrote:
> 2. Reference containers.
>
> These have classic reference semantics (à la Java). Internally, they may
> be implemented either as class objects or as reference counted structs.
>
> They're by default mutable. Qualifiers should apply to them gracefully.
>
> 3. Eager value containers.
>
> These are STL-style. Somewhat surprisingly I think these are the worst
> of the pack; they expensively duplicate at the drop of a hat and need to
> be carefully passed around by reference lest performance silently drops.
> Nevertheless, when used as members inside other data structures value
> semantics might be the appropriate choice. Also, thinking of them as
> values often makes code simpler.
>
> By default eager value containers are mutable. They should support
> immutable and const meaningfully.
For which containers we want to support is "2." not a (wrapper around a) pointer to "3."?
|
October 21, 2015 Re: Kinds of containers | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russel Winder | On Wednesday, 21 October 2015 at 15:34:14 UTC, Russel Winder wrote: > On Wed, 2015-10-21 at 14:06 +0000, Jonathan M Davis via Digitalmars-d wrote: >> [...] > […] >> [...] > > I am confused as to why you would find these functional/persistent data structures to be a problem in functional languages. The whole point of them is they can be easily shared without any locks. In effect each amendment of the structure creates a fork (effectively a copy but without the copy bit) of it. This goes hand in hand with the functional, and especially tail recursive computational model. > > The Groovy team is currently debating adding persistent data structures to the Groovy armoury. Clojure and Scala have had persistent data structures for ages. The Clojure ones are really good. The Scala ones have some issues so there is currently discussion of a third rewrite. > >> [...] > > Except in a concurrent and parallel world! For big mutable data structures you end up creating agents (or so we can use hyper trendy nomenclature, pico-services) to avoid messing around with locks, mutexes, etc. the use of which generally kills performance. > >> [...] > […] > > As noted in an earlier email, I am not yes convinced by this one. > >> [...] > > I'd say the opposite. I would suggest that persistent/functional data structures would be more generally useful than COW ones. > >> [...] > > I am not convinced by this argument. Yes these are the type of containers C++/D/… programmers are used to and have a lot of code using, but are they really the ones that should be used. Experience from Clojure and Scala is that persistent/functional data structures lead to much nicer/easier concurrent and parallel code. > > [...] > > These are where it's at IMHO. 99.999999% this is what makes sense. Except in a concurrent and parallel world! For big mutable data structures you end up creating agents (or so we can use hyper trendy nomenclature, pico-services) to avoid messing around with locks, mutexes, etc. the use of which generally kills performance. I actually find these annoying in C#. People modify things they shouldn't be all of the time. Which makes errors so much more prevalent. Every property that returns a List<T> is frequently exposed for the world to use. The best workaround I've found is to use interfaces that restrict what methods they have access to, but even then people resort to just casting it away. I'd prefer immutable by default. Personally. |
Copyright © 1999-2021 by the D Language Foundation