May 11, 2008
Janice Caron wrote:
> 
>>  This is why C++ is adding the
>>  notion of concepts (which facilitate compile time OOP)
> 
> So is D. Shall we take bets on which language gets them first, or in
> which language they will be most powerful?

Is it?  If so that's great.  Last comment I recall from Walter on the subject was that he believed D already had something as good as concepts.  I can't recall what it was exactly -- IIRC he said something like D doesn't need concepts because it has "static if".  I can't seem to find the thread where he said that, though.

--bb
May 11, 2008
On 11/05/2008, Simen Kjaeraas <simen.kjaras@gmail.com> wrote:
> On Sat, 10 May 2008 07:59:23 +0200, Janice Caron <caron800@googlemail.com> wrote:
>
> > 1) convert or downcast: cast(Foo)bar
> > 2) reinterpret: cast!(Foo)bar
> > 3) constancy: cast(invariant)bar, cast(!const)bar
> >
>
>  For 3), what if I want to cast const(int*) to const(int)*?
> cast(const(int)*)cast(!const)foo?

Wow, we're back on topic again! Woo hoo!

Good question.

My first thought would be

    const(int*) newPtr = cast(!const)oldPtr;

that is, remove all the constancy, then put it back again. Another possibility is:

    auto newPtr = cast(const(int)*)cast(!const)oldPtr;

which is exactly what you suggested. But neither are particularly ideal, so it looks like an ammendment to the proposal is in order. So my new suggestion would be, to remove only /part/ of the constancy, one of the following:

    cast(!const, T)x
    cast(T, !const)x

Whichever people like best. A third possibility would be to (almost) follow in the footsteps of C++, with

    const cast(T)x

which I kinda like, except that it makes specifying T mandatory, when often you wouldn't need to.
May 11, 2008
you miss the point. it's not just a matter of syntax it's a matter of
better design strategy. yes, the syntax is a personal preference, but
that preference is used to emphasize the design goal of proper
encapsulation. Also, the template solution has the same power and all
those examples you gave can be accomplished in both ways. nothing stops
me from using:
array.sort(SwapStrategy.stable, dg);

also you can use functions instead of delegates too (although, I really don't see what's the point of that besides maybe some questionable performance issues)

D already has a feature to allow me to define a free function that takes
an array as first parameter, and use it as array.Fn(other params);
this notion will extended in D2. so the issue here is hardy the syntax
itself (as the two forms are interchangeable).

Also I do realize that since arrays are builtins they cannot implement
interfaces. IMO, the best solution is to make all type behave like
objects via syntax sugar. D is already half way there, since you can do
int.max and such.
My point is that I separate the interface exposed to the user ( the
syntax of D) from the implementation. in order to make "hello world" or
34 behave like objects you don't have to add overhead. 34 can remain the
same int it is in C. you just need to add to the compiler the ability to
treat those as objects, that's the whole beauty of a compiled language,
you can perform some transformations by the compiler so that numbers and
strings and other builtins will behave like objects without the added
cost you get in a language like Ruby for that functionality.

My mantra is: use the right tool for the job. That means that although
templates are a useful tool, I don't think it's the only tool D provides
that should be used to solve all problems.
IMO, this problem is better solved with polymorphism and proper
encapsulation.

--Yigal
May 11, 2008
Janice Caron wrote:
> On 11/05/2008, Yigal Chripun <yigal100@gmail.com> wrote:
>>  also, what if a doesn't actually have a "member" member? this is still
>>  syntactically valid D code, but again I think the error would be
>>  reported elsewhere.
> 
> On that point, I concede. The reporting of template errors could certainly be improved. More than once I have wanted to see the "stack trace" of errors, working back from the innermost, to the line of code that ultimately triggered it.
> 
> However, that it not an argument against templates, it is an argument for improved error reporting. And hopefully, one day we'll get that.
> 
> This is not the first time that you've argued that some feature or strategy is bad because today's D compiler isn't good enough, but you need to remember that tomorrow's D compiler will be better. That's life on the cutting edge.

are we now debating the "sufficiently smart compiler" problem? ;)
I'm just kidding. But seriously, That is just another symptom of the
issue. the main thing is that the STL design of a collection of generic
templates is wrong, for all the reasons I've stated. C++ experts have
this mindset of all we got is a hammer(templates), so all the problems
are nails" which is simply not true.
templates are a useful feature which unfortunately is greatly overused.

The same thing can be said above Java and configuration files in XML. Java programmer sometimes you some Java code to glue their XML configuration files. Again, nothing's wrong with xml itself. XML is a grea thing when used properly, the issue is that not all problems can be solved with XML configuration files.

Maybe your rich C++ experience affects your judgment regarding this?

--Yigal
May 11, 2008
Simen Kjaeraas wrote:
> 
> For 3), what if I want to cast const(int*) to const(int)*?
> cast(const(int)*)cast(!const)foo?
> 
> -- Simen

that looks correct.
May 11, 2008
On 11/05/2008, Yigal Chripun <yigal100@gmail.com> wrote:
> you miss the point.

I may be missing /a/ point, but I doubt I'm missing /the/ point. Who gets to decide what /the/ point is anyway? Is there even a "the" point to begin with?


> it's not just a matter of syntax it's a matter of
>  better design strategy.

Who gets to define "better"? Seems to me that Phobos has the better design strategy, but that's just personal opinion, with no more to back it up than yours. But still - the template strategy seems to offer /everything/ that polymorphism offers (including polymorphism), and more.



>  Nothing stops me from using:
>  array.sort(SwapStrategy.stable, dg);

Except that your interface must expose that function signature, which in turn means that all implementors of that interface must support that function. Interfaces cannot provide implementations, so although you could supply a mixin, there is no guarantee that all implementors will use it.


>  Also I do realize that since arrays are builtins they cannot implement
>  interfaces. IMO, the best solution is to make all type behave like
>  objects via syntax sugar. D is already half way there, since you can do
>  int.max and such.

OK, so you propose adding new features to the language, just so that you don't have to use templates? The prevailing wisdom is, if it can be done with a library solution, it should; if not, then maybe we need new syntax. But we don't generally get new syntax just because some people don't like templates, so I suspect that that is unlikely to happen.


>  My point is that I separate the interface exposed to the user ( the
>  syntax of D) from the implementation.

So does std.algorithm.

std.algorithm provides container-independent, sort-criterion-independent, algorithms. Plug in container or sort-criterion of your choice. The container only needs to expose iterators, and all of std.algorithm's functions will work.


>  My mantra is: use the right tool for the job. That means that although
>  templates are a useful tool, I don't think it's the only tool D provides
>  that should be used to solve all problems.
>  IMO, this problem is better solved with polymorphism and proper
>  encapsulation.

Again, who gets to define "better"?
May 11, 2008
Janice Caron wrote:
> On 11/05/2008, Simen Kjaeraas <simen.kjaras@gmail.com> wrote:
>> On Sat, 10 May 2008 07:59:23 +0200, Janice Caron <caron800@googlemail.com> wrote:
>>
>>> 1) convert or downcast: cast(Foo)bar
>>> 2) reinterpret: cast!(Foo)bar
>>> 3) constancy: cast(invariant)bar, cast(!const)bar
>>>
>>  For 3), what if I want to cast const(int*) to const(int)*?
>> cast(const(int)*)cast(!const)foo?
> 
> Wow, we're back on topic again! Woo hoo!
> 
> Good question.
> 
> My first thought would be
> 
>     const(int*) newPtr = cast(!const)oldPtr;
> 
> that is, remove all the constancy, then put it back again. Another possibility is:
> 
>     auto newPtr = cast(const(int)*)cast(!const)oldPtr;
> 
> which is exactly what you suggested. But neither are particularly ideal, so it looks like an ammendment to the proposal is in order. So my new suggestion would be, to remove only /part/ of the constancy, one of the following:
> 
>     cast(!const, T)x
>     cast(T, !const)x
> 
> Whichever people like best. A third possibility would be to (almost) follow in the footsteps of C++, with
> 
>     const cast(T)x
> 
> which I kinda like, except that it makes specifying T mandatory, when often you wouldn't need to.

another option would be to allow:
cast(const(T))x;
where you redefine the constancy of x and this would be subject to a
test that T is not a different type (except for constancy, of course).
that could simply be a syntax sugar for two separate semantic casts
(even if the compiler does it in one operation)
so, this would become:

const(int*) x = ...;
auto newX = cast(const(int)*)x;

the check is to take both types, remove all const/invariant and compare.
the result must be identical.
thus,
const(int*) => (int*) and const(int)* => (int)* which are identical.
put a different amount of stars, or replace int with long and the cast
fails.
I'm not sure if this is that important to add, since you can just use
two casts.

sprinkle this post with invariant where needed, to make this a full proposal.

--Yigal
May 11, 2008
On 11/05/2008, Yigal Chripun <yigal100@gmail.com> wrote:
> another option would be to allow:
>  cast(const(T))x;
>  where you redefine the constancy of x and this would be subject to a
>  test that T is not a different type (except for constancy, of course).

Oh, I see. So, cast(T) would double up for /either/ downcasting, /or/
removing constancy, but never both at the same time. (Both at the same
time would require two casts).

This is probably the best suggestion of all. I like it.
May 11, 2008
See my comments bellow.

Janice Caron wrote:
> On 11/05/2008, Yigal Chripun <yigal100@gmail.com> wrote:
>> you miss the point.
> 
> I may be missing /a/ point, but I doubt I'm missing /the/ point. Who gets to decide what /the/ point is anyway? Is there even a "the" point to begin with?
> 
> 
>> it's not just a matter of syntax it's a matter of
>>  better design strategy.
> 
> Who gets to define "better"? Seems to me that Phobos has the better design strategy, but that's just personal opinion, with no more to back it up than yours. But still - the template strategy seems to offer /everything/ that polymorphism offers (including polymorphism), and more.
> 
> 
I want to be able to do this:
List!(T) col = new LinkedList!(T);

and be able to change the linked list to a vector in the future (or any other suitable collection) with the changes limited to this line alone. is it possible with templates? c++ certainly does not provide this flexibility with STL.

> 
>>  Nothing stops me from using:
>>  array.sort(SwapStrategy.stable, dg);
> 
> Except that your interface must expose that function signature, which in turn means that all implementors of that interface must support that function. Interfaces cannot provide implementations, so although you could supply a mixin, there is no guarantee that all implementors will use it.

huh?
the point of interfaces is to define relations so that I can be sure
that I can sort all sortable collections for instance. the fact that
it's in the interface dictates that all derived classes have to provide
some way to do that. I don't care as a user how that is implemented, I
just know I can sort my sortable collection since it's in the interface.
another benefit is that each derived class can provide a specialized and
optimized version for its own internal structure. Iterators add overhead
here for no reason. oh there is one reason, generality... right?

> 
> 
>>  Also I do realize that since arrays are builtins they cannot implement
>>  interfaces. IMO, the best solution is to make all type behave like
>>  objects via syntax sugar. D is already half way there, since you can do
>>  int.max and such.
> 
> OK, so you propose adding new features to the language, just so that you don't have to use templates? The prevailing wisdom is, if it can be done with a library solution, it should; if not, then maybe we need new syntax. But we don't generally get new syntax just because some people don't like templates, so I suspect that that is unlikely to happen.
> 
actually, I'm talking about existing D features and Walter's plans to enhance them which where announced in last year's conference. I'm merely stating that those changes would be useful here, I didn't propose anything new here.

> 
>>  My point is that I separate the interface exposed to the user ( the
>>  syntax of D) from the implementation.
> 
> So does std.algorithm.
> 
> std.algorithm provides container-independent, sort-criterion-independent, algorithms. Plug in container or sort-criterion of your choice. The container only needs to expose iterators, and all of std.algorithm's functions will work.

again, Iterators can be useful for some tasks, but they are inherently limited. I've read an article once about that topic, if I can find it, I'll post it here. the gist is that there are more general and better solutions than iterators. C++ uses iterators extensively simply because it has no other choice since it lacks the needed language support for other approaches. fortunately, D does provide all the necessary language support, so we are not limited by iterators any more. :)
> 
> 
>>  My mantra is: use the right tool for the job. That means that although
>>  templates are a useful tool, I don't think it's the only tool D provides
>>  that should be used to solve all problems.
>>  IMO, this problem is better solved with polymorphism and proper
>>  encapsulation.
> 
> Again, who gets to define "better"?
May 11, 2008
Janice Caron Wrote:

> On 11/05/2008, Yigal Chripun <yigal100@gmail.com> wrote:
> > another option would be to allow:
> >  cast(const(T))x;
> >  where you redefine the constancy of x and this would be subject to a
> >  test that T is not a different type (except for constancy, of course).
> 
> Oh, I see. So, cast(T) would double up for /either/ downcasting, /or/
> removing constancy, but never both at the same time. (Both at the same
> time would require two casts).
> 
> This is probably the best suggestion of all. I like it.

Actually, we got it slightly wrong:
since D defines removal of invariance as undefined behavior and since casting with constancy does not convert the value but rather changes the type tag of the value, it would be more appropriate to use the "cast!" form, IMO.
it feels more appropriate to me cause it warns the user that it's his responsibility if something breaks. That's the entire reason for the const! form, isn't it?

--Yigal