www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Safer casts

reply "Janice Caron" <caron800 googlemail.com> writes:
Better, safer casts have been requested before - in fact I routinely
see that request on the monthly "D wishlist" that goes round. But
here's a fairly concrete suggestion as to what the syntax might be.
And as a bonus, we'd get rid of the "cast" keyword, thus reducing the
keyword count by one. So, here are the various types of transformation
I suggest:


(*) Build a new instance with a different memory layout, but with the
same value. (For example, casting double to int). For lossless
conversions, this can happen implicitly, but for lossy conversions, an
explicit cast is necessary. For D, I suggest

    to!(T)(x)

where to! is a template function provided by std.conv (not a keyword).
to! already performs many conversions. Throwing in the basics like
to!(int)(double) would be an obvious extension.



(*) Use RTTI to cast up and down the class heirarchy. In C++, this
would be dynamic_cast<T>(x). For D, I suggest two things. Firstly, the
cast:

    class!(T)(x)

to do that actual upcast or downcast - but RTTI dynamic casts can
fail, so the question arises: What should we do if the cast fails?
Should we return null, or should we throw an exception. My view is
that we should throw an exception, but also introduce a new construct
to test whether or not the cast would be possible in the first place:

    is!(T)(x)

which returns bool. is!(T) would be like Java's instanceof. Thus, one
could write

    void f(A a)
    {
        if (is!(B)a)
        {
            B b = class!(B)(a);
            /*...*/
        }
        else if (is!(C)a)
        {
            C c = class!(C)(a);
            /*...*/
        }
    }

etc.


(*) Reinterpret a bit pattern. In C++, this would be
reinterpret_cast<T>(x). Without changing the memory layout, or the
constancy, reinterpret the bits to mean something else. For D, I
suggest

    union!(T)(x)


(*) Change const to mutable, or invariant to mutable. In C++, this
would be const_cast<T>(x). There is no equivalent in D, however, in D,
one can currently write cast(T)x, and constancy will be magically (and
dangerously) waved away. In the new scheme, I suggest:

    auto!(T)(x)



(*) Change const to invariant, or mutable to invariant. Currently, the
syntax for this is cast(invariant)x, however this has two
disadvantages: (i) the entire type is made invariant, and you might
want to invariantize only part of the type, and (ii) cast(T)x also
works. In other words, (i) is not powerful enough, and (ii) is too
dangerous. There is no equivalent for this in C++, since C++ has no
invariance. For D, I suggest:

    invariant!(T)(x)



I think that's all bases covered. So in summary we'd have:

    to!(T)(x)        // convert
    is!(T)(x)        // is upcast or downcast possible?
    class!(T)(x)    // upcast or downcast
    union!(T)(x)    // reinterpret the bits
    auto!(T)(x)        // remove constancy
    invariant!(T)(x)    // add invariance


Those seem reasonably obvious word choices to me. Thoughts?
May 09 2008
next sibling parent reply terranium <spam here.lot> writes:
Janice Caron Wrote:

     to!(T)(x)        // convert
     is!(T)(x)        // is upcast or downcast possible?
     class!(T)(x)    // upcast or downcast
     union!(T)(x)    // reinterpret the bits
     auto!(T)(x)        // remove constancy
     invariant!(T)(x)    // add invariance

1) I don't think you can throw away the cast keyword, this will break all existing code. 2) how about interfaces? 3) I believe reinterpret_cast was introduced as analog of C cast, D already has cast for this purpose. I don't think D needs union!(). 4) D already has cast to add/remove constancy/invariance. My proposal was just adding safe cast for downcasts with the syntax of existing cast, nothing more.
May 09 2008
parent terranium <spam here.lot> writes:
Janice Caron Wrote:

 I note
 that the name is now less apt.

 3) I believe reinterpret_cast was introduced as analog of C cast,

It was introduced to replace /one/ kind of transformation which old-style casts did, but not all of them. For example // C++ double x; int y = reinterpret_cast<int>(x); // won't compile int y = static_cast<int>(x); // OK D already has cast for /all/ of the purposes I listed, so you could argue that D doesn't need any of them. The point is, if you wanted to be explicit about exactly what kind of transformation you wanted, then you would need it.

My opinion is we don't need to be explicit when casting double to int. reinterpret_cast is used usually to cast to/from void* - that is for pointer types - here it has no difference with D cast. Adding new construct adds complexity and requires extra time to learn and extra info to keep in mind, I believe these constructs' safeness is minor and it's not worth its complexity.
 I wasn't disputing that. Consider this a separate proposal.

May 09 2008
prev sibling next sibling parent terranium <spam here.lot> writes:
and I like existing cast keyword, it's short enough to write and long enough to
spot.
May 09 2008
prev sibling next sibling parent terranium <spam here.lot> writes:
And I hate those exclamation marks and chains of brackets.
May 09 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/9 terranium <spam here.lot>:
 1) I don't think you can throw away the cast keyword, this will break all
existing code.

This is exactly the problem C++ had. C++ introduced new, better cast types: static_cast<T>, dynamic_cast<T>, const_cast<T>, and reinterpret_cast<T>. However, where C++ went wrong is they failed to outlaw the original cast - presumably for the same reason, that it would break existing code. The problem is that unless you outlaw the old "one cast for all purposes" cast, then people will continue to use it, when what you really want it to force everyone to migrate to the new, "safe" ways.
 2) how about interfaces?

They're a run-time thing, so is!(T) and class!(T) should work for interfaces just as for classes. This is where D differs from C++, of course, because we have interfaces where C++ has multiple inheritance. But yeah - just as C++'s dynamic_cast<T> works for multiple inheritance, so class!(T) should work for interfaces - though I note that the name is now less apt.
 3) I believe reinterpret_cast was introduced as analog of C cast,

It was introduced to replace /one/ kind of transformation which old-style casts did, but not all of them. For example // C++ double x; int y = reinterpret_cast<int>(x); // won't compile int y = static_cast<int>(x); // OK
 D already has cast for this purpose. I don't think D needs union!().

D already has cast for /all/ of the purposes I listed, so you could argue that D doesn't need any of them. The point is, if you wanted to be explicit about exactly what kind of transformation you wanted, then you would need it.
 4) D already has cast to add/remove constancy/invariance.

Again, D already has cast, which can do each and every transformation I listed. What it can't do is emit an error if the wrong kind of transformation is performed.
 My proposal was just adding safe cast for downcasts with the syntax of
existing cast, nothing more.

I wasn't disputing that. Consider this a separate proposal.
May 09 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/9 terranium <spam here.lot>:
 and I like existing cast keyword, it's short enough to write and long enough
to spot.

I like it too. But it has its disadvantages. For example, you might write a dynamic cast as void f(in A a) { B b = cast(B) a; if (b !is null) { /* ... */ } /*...*/ } and it would compile. Unfortunately, you have just accidently cast away const, and that kind of bug is hard to spot. But replace it with void f(in A a) { if (is!(B)(a)) { B b = class!(B)(a); /* ... */ } /*...*/ } and you have a compile-time error, because class!(T) cannot change const.into mutable. So I guess there's a tradeoff between "nice" syntax, like cast(T), and "safe" syntax. I'm just wondering if we should err on the side of safe
May 09 2008
prev sibling next sibling parent reply janderson <askme me.com> writes:
Janice Caron wrote:
 Better, safer casts have been requested before - in fact I routinely
 see that request on the monthly "D wishlist" that goes round. But
 here's a fairly concrete suggestion as to what the syntax might be.
 And as a bonus, we'd get rid of the "cast" keyword, thus reducing the
 keyword count by one. So, here are the various types of transformation
 I suggest:
 
 
 (*) Build a new instance with a different memory layout, but with the
 same value. (For example, casting double to int). For lossless
 conversions, this can happen implicitly, but for lossy conversions, an
 explicit cast is necessary. For D, I suggest
 
     to!(T)(x)
 
 where to! is a template function provided by std.conv (not a keyword).
 to! already performs many conversions. Throwing in the basics like
 to!(int)(double) would be an obvious extension.
 
 
 
 (*) Use RTTI to cast up and down the class heirarchy. In C++, this
 would be dynamic_cast<T>(x). For D, I suggest two things. Firstly, the
 cast:
 
     class!(T)(x)
 
 to do that actual upcast or downcast - but RTTI dynamic casts can
 fail, so the question arises: What should we do if the cast fails?
 Should we return null, or should we throw an exception. My view is
 that we should throw an exception, but also introduce a new construct
 to test whether or not the cast would be possible in the first place:
 
     is!(T)(x)
 
 which returns bool. is!(T) would be like Java's instanceof. Thus, one
 could write
 
     void f(A a)
     {
         if (is!(B)a)
         {
             B b = class!(B)(a);
             /*...*/
         }
         else if (is!(C)a)
         {
             C c = class!(C)(a);
             /*...*/
         }
     }
 
 etc.
 
 
 (*) Reinterpret a bit pattern. In C++, this would be
 reinterpret_cast<T>(x). Without changing the memory layout, or the
 constancy, reinterpret the bits to mean something else. For D, I
 suggest
 
     union!(T)(x)
 
 
 (*) Change const to mutable, or invariant to mutable. In C++, this
 would be const_cast<T>(x). There is no equivalent in D, however, in D,
 one can currently write cast(T)x, and constancy will be magically (and
 dangerously) waved away. In the new scheme, I suggest:
 
     auto!(T)(x)
 
 
 
 (*) Change const to invariant, or mutable to invariant. Currently, the
 syntax for this is cast(invariant)x, however this has two
 disadvantages: (i) the entire type is made invariant, and you might
 want to invariantize only part of the type, and (ii) cast(T)x also
 works. In other words, (i) is not powerful enough, and (ii) is too
 dangerous. There is no equivalent for this in C++, since C++ has no
 invariance. For D, I suggest:
 
     invariant!(T)(x)
 
 
 
 I think that's all bases covered. So in summary we'd have:
 
     to!(T)(x)        // convert
     is!(T)(x)        // is upcast or downcast possible?
     class!(T)(x)    // upcast or downcast
     union!(T)(x)    // reinterpret the bits
     auto!(T)(x)        // remove constancy
     invariant!(T)(x)    // add invariance
 
 
 Those seem reasonably obvious word choices to me. Thoughts?

I think that up casting for classes should be implicit, like it works in C++. Its perfectly ok to upcast and there shouldn't be any warning casts around that. That is actually a very useful feature for genetic style coding. Of course the cast should still work for upcasting, but its superfluous. -Joel
May 09 2008
parent Robert Fraser <fraserofthenight gmail.com> writes:
janderson wrote:
 Janice Caron wrote:
 Better, safer casts have been requested before - in fact I routinely
 see that request on the monthly "D wishlist" that goes round. But
 here's a fairly concrete suggestion as to what the syntax might be.
 And as a bonus, we'd get rid of the "cast" keyword, thus reducing the
 keyword count by one. So, here are the various types of transformation
 I suggest:


 (*) Build a new instance with a different memory layout, but with the
 same value. (For example, casting double to int). For lossless
 conversions, this can happen implicitly, but for lossy conversions, an
 explicit cast is necessary. For D, I suggest

     to!(T)(x)

 where to! is a template function provided by std.conv (not a keyword).
 to! already performs many conversions. Throwing in the basics like
 to!(int)(double) would be an obvious extension.



 (*) Use RTTI to cast up and down the class heirarchy. In C++, this
 would be dynamic_cast<T>(x). For D, I suggest two things. Firstly, the
 cast:

     class!(T)(x)

 to do that actual upcast or downcast - but RTTI dynamic casts can
 fail, so the question arises: What should we do if the cast fails?
 Should we return null, or should we throw an exception. My view is
 that we should throw an exception, but also introduce a new construct
 to test whether or not the cast would be possible in the first place:

     is!(T)(x)

 which returns bool. is!(T) would be like Java's instanceof. Thus, one
 could write

     void f(A a)
     {
         if (is!(B)a)
         {
             B b = class!(B)(a);
             /*...*/
         }
         else if (is!(C)a)
         {
             C c = class!(C)(a);
             /*...*/
         }
     }

 etc.


 (*) Reinterpret a bit pattern. In C++, this would be
 reinterpret_cast<T>(x). Without changing the memory layout, or the
 constancy, reinterpret the bits to mean something else. For D, I
 suggest

     union!(T)(x)


 (*) Change const to mutable, or invariant to mutable. In C++, this
 would be const_cast<T>(x). There is no equivalent in D, however, in D,
 one can currently write cast(T)x, and constancy will be magically (and
 dangerously) waved away. In the new scheme, I suggest:

     auto!(T)(x)



 (*) Change const to invariant, or mutable to invariant. Currently, the
 syntax for this is cast(invariant)x, however this has two
 disadvantages: (i) the entire type is made invariant, and you might
 want to invariantize only part of the type, and (ii) cast(T)x also
 works. In other words, (i) is not powerful enough, and (ii) is too
 dangerous. There is no equivalent for this in C++, since C++ has no
 invariance. For D, I suggest:

     invariant!(T)(x)



 I think that's all bases covered. So in summary we'd have:

     to!(T)(x)        // convert
     is!(T)(x)        // is upcast or downcast possible?
     class!(T)(x)    // upcast or downcast
     union!(T)(x)    // reinterpret the bits
     auto!(T)(x)        // remove constancy
     invariant!(T)(x)    // add invariance


 Those seem reasonably obvious word choices to me. Thoughts?

I think that up casting for classes should be implicit, like it works in C++. Its perfectly ok to upcast and there shouldn't be any warning casts around that. That is actually a very useful feature for genetic style coding. Of course the cast should still work for upcasting, but its superfluous. -Joel

I think Janice mentioned that it would work exactly like that.
May 09 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 09/05/2008, janderson <askme me.com> wrote:
  I think that up casting for classes should be implicit for up cast, like it
 works in C++.  Its perfectly ok to upcast and there shouldn't be any warning
 casts around that.  That is actually a very useful feature for genetic style
 coding.  Of course the cast should still work for upcasting, but its
 superfluous.

Yes, you are completely correct, of course.
May 09 2008
prev sibling next sibling parent reply BCS <BCS pathlink.com> writes:
Janice Caron wrote:
 (*) Use RTTI to cast up and down the class heirarchy. In C++, this
 would be dynamic_cast<T>(x). For D, I suggest two things. Firstly, the
 cast:
 
     class!(T)(x)
 
 to do that actual upcast or downcast - but RTTI dynamic casts can
 fail, so the question arises: What should we do if the cast fails?
 Should we return null, or should we throw an exception. My view is
 that we should throw an exception, but also introduce a new construct
 to test whether or not the cast would be possible in the first place:
 
     is!(T)(x)
 
 which returns bool. is!(T) would be like Java's instanceof. Thus, one
 could write
 
     void f(A a)
     {
         if (is!(B)a)
         {
             B b = class!(B)(a);
             /*...*/
         }
         else if (is!(C)a)
         {
             C c = class!(C)(a);
             /*...*/
         }
     }
 
 etc.
 

The major issue I have with this is that the construct that actually does the downcast MUST also do the type check. Therefor it gets done twice and this is a bit of a performance issue. (and performance snobs will start finding way to skip the second test (like your union) and then get it wrong.) I'd rather see a testClass!(T) version that acts exactly like the current cast and a isClass!(T) that acts like your class!(T).
May 09 2008
next sibling parent reply BCS <BCS pathlink.com> writes:
Janice Caron wrote:
 On 09/05/2008, Janice Caron <caron800 googlemail.com> wrote:
 
  I'd rather see a testClass!(T) version that acts exactly like the current
 cast and a isClass!(T) that acts like your class!(T).

OK, so how about just making class!(T) return null, and do away with is!(T)? That's what C++ does with dynamic_cast<T>, after all.

One goal of the proposal was to avoid introducing more keywords. Some of these conversions don't have to be keywords, of course. to! is already implemented in std.conv, and this proposal only suggests extending its reach. But others kinds of transformation are better done by the compiler, and I'd say that includes RTTI, so for that reason, I went for keyword reuse.

I think it you go with the __!(T) syntax it would be better to /not/ used keywords at all. Make them either "magic" template that are defined in the compiler or as part of the compiler specific runtime.
May 09 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
I too do not like the proposed syntax. I'd suggest the following scheme:
1) convert: cast(foo)bar
2) reinterpret: cast!(foo)bar
3) constancy: cast(invariant), cast(mutable)
4) downcast: cast!(foo)bar

explanation:
all casts use the word "cast" so it's easy to grep for. more dangerous
casts use the "cast!" form. reinterpret and downcast both do the same
thing therefore there's no need to separate the two (both do not change
the actual memory). Constancy is cast explicitly with its own cast instance.

example of use:
invariant int a = ...;
double a = cast!(double)cast(mutable)(b);

the above removes invariance and then reinterprets the bit pattern to
double.

class B : A {}
class C : A {}
A b = new B;
C c = cast!(C)b; // c is null

cast! is more dangerous and thus can fail and return a null as in the
above example. OTOH, cast is safe and expected to always work, so if you
use cast instead of cast! to downcast, an exception will be thrown.

have I left anything out?

-- Yigal

PS - note that my proposal only adds the "cast!" form to D.
cast(invariant) is already defined in D, and cast(mutable) can be
replaced by cast(!invariant) although it's less readable IMO.
May 09 2008
next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Janice Caron Wrote:

 On 10/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 I too do not like the proposed syntax. I'd suggest the following scheme:
  1) convert: cast(foo)bar
  2) reinterpret: cast!(foo)bar
  3) constancy: cast(invariant), cast(mutable)
  4) downcast: cast!(foo)bar

Your proposal is better than mine, but I think I can improve it even more: 1) convert: cast(Foo)bar 2) reinterpret: cast!(Foo)bar 3) constancy: cast(invariant)bar, cast(!const)bar 4) downcast: cast(Foo)bar

With much interest I followed this thread. Forgive my ignorance, but I can not understand one thing. Could this functionality be implemented as library functions all using the built in cast? The syntax will be clumsier though. I see from std.typecons and std.typetraits that D has powerful meta programming. Functions should be able to detect different cases. Coding standards could prescribe that cast must not be used and only the library functions can be used. Dee Girl
May 09 2008
parent reply Dee Girl <deegirl noreply.com> writes:
Janice Caron Wrote:

 On 10/05/2008, Dee Girl <deegirl noreply.com> wrote:
 With much interest I followed this thread. Forgive my ignorance, but I can not
understand one thing. Could this functionality be implemented as library
functions all using the built in cast?

Not really, since part of the proposal is that const c = new C; auto d = cast(C)c; // ERROR - cast cannot remove constancy should not compile. I see ruling this out as an important part of const correctness. If you make it optional (e.g. have some sort of template, static_cast!(T)), then people are just going to write "cast" instead of "static_cast!" because (a) it's shorter, and (b) the programmer's inherent belief that everything they write is bug free. That's exactly what happens in C++.

But in C++ I can not grep for old-style cast. In D I can (but see my other message...) Another thing I noticed: class C {} void main(string[] args) { auto c = new const C; } Does not compile. But if I change to const(C) it does compile. It is a bit unusual that parens make such a big difference. Thank you, Dee Girl
May 09 2008
parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
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? -- Simen
May 11 2008
next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
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
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
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

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
parent reply Yigal Chripun <yigal100 gmail.com> writes:
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
May 11 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 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.

Hmmm. Perhaps. But then you lose the distinction between const_cast and reinterpret_cast. With cast!() acting as reinterpret_cast, I can do: void foo(Something x) { auto y = cast!(Anything)x; ... } and it would always succeed, and you can't say "unless there's a change of constancy" in this case, because the before and after might have completely different memory layouts, so there'd be no way even to tell whether or not that had happened. I can see that const_cast and dynamic_cast could share the same syntax, but I really don't see how reinterpret_cast could double up with /anything/ else. So maybe that brings us to this: cast(T)x // dynamic_cast cast!(T)x // reinterpret_cast static cast(T)x // static_cast const cast(T)x // const_cast That does have a certain appeal to it.

a different meaning, wouldn't it create ambiguity in the language? --Yigal since i don't classify the casts as you do, I'm not sure I agree with the above. didn't you yourself complain that cast(T) can remove constancy by mistake and create bugs? why would you allow cast!(T) to do just that? anyway, I need to think more about that. I haven't arrived at a conclusion yet, so I'd appreciate more examples/explanations. Also, My definition was that cast() can change the memory while cast!() doesn't. it seems you define them in reverse of that. --Yigal
May 11 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
<snip>
if interpret_cast can be implemented by the user and it is quite
dangerous, than i think people who need that should implement that
themselves with union as you showed. no need for D to provide that
functionality by default.
I'm still not sure about constancy casts. but in order to provide the
minimal solution, I'd prefer cast!(T) to do constancy casts over adding
another construct for that. also, I don't like the static cast(), and it
might cause ambiguity problems. what is this good for?

open question: if this proposal does not provide a reinterpret_cast, can
we split the cast!(T) to perform either a type change /or/ a constancy
change?

int n = ...;
auto d1 = cast(double)n; //converts the n to double so 1 => 1.0
// below case uses the same bit pattern, exception on invalid double
// value
auto d2 = cast!(double)n;

same goes for classes with user defined cast and cast! operators.
downcast can be treated as a compiler pre-defined conversion from a
class to its derived classes. uses the cast(Derived) form and throws
exception on error.
constancy is performed via cast!(T).
pointers are cast with cast!(T)
so to convert const(int)* to const(long*) you'll need something like:
auto res = cast!(const(long*))cast(const(long)*)x;
May 11 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
<snip>
I agree on mostly everything, I'm still not convinced about the rtti
cast not throwing on error, though.
I'll just add that cast! won't be used to generally change types but
since we added _specific_ cases that cast! handles, it's worth
considering adding another special case which is the static_cast for
classes.
May 11 2008
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Simen Kjaeraas" wrote
 On Sat, 10 May 2008 07:59:23 +0200, Janice Caron 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? -- Simen

I'm not really joining this discussion (yet), but I just wanted to respond to this. If the purpose of your cast is to assign to a const(int) * variable, you need no casts to do this. const(int*) implicitly casts to const(int)*: int m = 5; const(int *) ptr = &m; const(int)* ptr2 = ptr; // OK Why? Because I can't change anything through ptr2 that I couldn't change through ptr. Now, if you casting to be able to rebind ptr, then I think you would need some crazy casting like you stated. -Steve
May 12 2008
parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
Steven Schveighoffer <schveiguy yahoo.com> wrote:

 Now, if you casting to be able to rebind ptr, then I think you would need
 some crazy casting like you stated.

 -Steve

Actually, I had no other intent than to see if I could break the system. For a better (or worse, mayhaps) version: const(C*)* (or whatever, really) to const(invariant(C)*)*. For this, I'd prefer Janice's suggestion of const cast, as I understand it: const(C*)* foo; cast(const(invariant(C)*)*)foo; // error, cast can't change constancy const cast(const(invariant(C)*)*)foo; // works like a charm The const cast looks good to me, whereas what I used in the last example was downright ugly. Casting to a different constancy should be one cast, not first making it all mutable, and then put const back in there. -- Simen
May 12 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Simen Kjaeraas wrote:
 Steven Schveighoffer <schveiguy yahoo.com> wrote:
 
 Now, if you casting to be able to rebind ptr, then I think you would need
 some crazy casting like you stated.

 -Steve

Actually, I had no other intent than to see if I could break the system. For a better (or worse, mayhaps) version: const(C*)* (or whatever, really) to const(invariant(C)*)*. For this, I'd prefer Janice's suggestion of const cast, as I understand it: const(C*)* foo; cast(const(invariant(C)*)*)foo; // error, cast can't change constancy const cast(const(invariant(C)*)*)foo; // works like a charm The const cast looks good to me, whereas what I used in the last example was downright ugly. Casting to a different constancy should be one cast, not first making it all mutable, and then put const back in there. -- Simen

if I'm not mistaken, the other example was both a cast of constancy and of type. that needs two casts (that's one of the main principles of this proposal). in the latest version of this proposal, the above would be: const(C*)* foo; cast!(const(invariant(C)*)*)foo; // works like a charm a cast without the ! is not defined for constancy casts, so the compiler would err and suggest using cast! instead (maybe with a message than warn the user that this can be dangerous)
May 12 2008
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
thanks for the added examples and explanations to the proposal.
I want to note two things though:
a) I did suggest cast(!invariant) instead of cast(mutable) but I'm not
sure it's more readable. maybe cast(!const) is a bit better (and
shorter) but the lack of symmetry bothers me.
b) Downcasts - the danger with them is that they can fail if you
downcast to the wrong derived class. that is why I've suggested:
class B : A;
class C : A;
A b = new B;
C c1 = cast!(C)b;
C c2 = cast(C)b;

the first cast will fail and return a null. the exclamation mark should
remind the programmer that this can happen and therefore it's his
responsibility to check the returned value of the cast. This is current
D behavior.

OTOH, the second cast is a "safe" cast and must always work, therefore
the failure to cast will result in an exception being thrown.

another thing: the second cast does not have to fail if the user defined
 his own cast from B to C.

"cast" can be defined by the user, but I'm not sure whether it would be
a good idea to allow the same for "cast!"
what do you think?

--Yigal
May 09 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
I do not agree with the premise that a reinterpret cast always succeeds,
even the C++ version can throw an exception!

maybe, my use of the word "fail" was not accurate. let me re-phrase my
thoughts:
there are only two ways to coerce one type into another:
a) you convert the value (i.e you change the underlying memory) - this
should always produce the expected result [I mark this with cast(T)]
b) you do _not_ convert the value, you just change the type tag attached
to the value - this can produce bad results which do not conform to the
underlying memory scheme of the type. [I mark this with cast!(T)]
let's look at an example of the latter:
int a = ...;
auto c = cast!(dchar) a;

suppose the bit layout of a is illegal utf-32 encoding. would you prefer
D allowed storing such an illegal value in a dchar?
IMO, a strongly typed language (like D) must enforce at all times that
its variables are valid. I do not want D to allow storing illegal values
like that. that must be an error.

Am I making any sense?

--Yigal
May 10 2008
next sibling parent reply terranium <spam here.lot> writes:
Janice Caron Wrote:

 Consider this:
 
     string s = "\u20AC"; /* s contains exactly one Unicode character */
     string t = s[1..2];

this makes string array of bytes rather than chars.
May 10 2008
parent terranium <spam here.lot> writes:
Janice Caron Wrote:

 If you want to talk Unicode

I'll let others to have the pleasure to discover this themselves :)))
May 10 2008
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
IMHO, your reply makes perfect sense for C/C++ but not for D.
specifically because D has other facilities to handle those cases.
a dchar (or [w]char) _must_ always contain valid data. if you need to
store other encodings you can use ubyte instead which does not limit you
to a specific bit pattern (this is why D has it in the first place...)
the above example of slices can be easily dealt with since (unlike in
C/C++) D arrays know their length. this is similar to the fact that D
checks bounds on arrays and throws on error (in debug mode) whereas
C/C++ does not. IMO, the D implementation itself (both the compiler and
the runtime) need to make sure chars are always valid. this should not
be something optional added via a library.

I agree with you notion of levels, I just think D provides for much
better facilities for low-level coding compared to using unsafe C/C++
conventions.

    int n;
    dchar c = cast(dchar)n;
    dchar d = cast!(dchar)n;

in the above code, the second one should be used and it might throw. the
first simply does not make any sense and should produce a compiler error
because you cannot convert an int value to a dchar (unless it's a one
digit int)

<off topic rant>
What worries me most about D is the fact that D becomes an extension to C++.
The whole idea behind D was to create a new language without all the
baggage and backward compatibility issues of C++.
I don't want a slightly more readable version of C++ since I'll get that
with C++0x.
c++ programmers want D to have a D "stl" and a D boost. that's wrong!
STL is badly designed and employs massive amounts of black magic that
ordinary people do not understand. (I suffer at work while writing in
C++). in what world does it make sense to mark an abstract method with
"=0;" at the end, especially when the method is horizontally long and
that gets to be off screen!
D should be written with a D mindset which should be the best
ingredients extracted from all those languages D got its influences
from: java, C#, python, ruby, c/c++, etc. Tango is a good example of
designing such a new D mindset, IMO. Phobos is not, since it's merely C
code written with D syntax, with all those new shiny code Andrei added
which is C++ code written with D syntax. I appreciate his great
expertise in C++, but I already can use C++ libraries in C++ without
learning a new language. D needs to be better. *much* better.
</rant>

--Yigal
May 10 2008
next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 <off topic rant>
 What worries me most about D is the fact that D becomes an extension to C++.
 The whole idea behind D was to create a new language without all the
 baggage and backward compatibility issues of C++.
 I don't want a slightly more readable version of C++ since I'll get that
 with C++0x.
 c++ programmers want D to have a D "stl" and a D boost. that's wrong!
 STL is badly designed and employs massive amounts of black magic that
 ordinary people do not understand. (I suffer at work while writing in
 C++). in what world does it make sense to mark an abstract method with
 "=0;" at the end, especially when the method is horizontally long and
 that gets to be off screen!
 D should be written with a D mindset which should be the best
 ingredients extracted from all those languages D got its influences
 from: java, C#, python, ruby, c/c++, etc. Tango is a good example of
 designing such a new D mindset, IMO. Phobos is not, since it's merely C
 code written with D syntax, with all those new shiny code Andrei added
 which is C++ code written with D syntax. I appreciate his great
 expertise in C++, but I already can use C++ libraries in C++ without
 learning a new language. D needs to be better. *much* better.
 </rant>

I want to understand what you said because it can change my decision to use D1 or D2 or not at all. From what I read in Phobos some good examples of interesting D mindset that are hard in C++ or other languages are std.typecons and std.algorithm. There are parts of Phobos that look very ugly. For example the streams. I used STL and it is useful to me. If it is true for many people then why D not take the good parts of it? If you know of bad designs in STL they could be avoided in D. Also STL has many balck magic but it is because of C++ imperfections. The definition of STL is mathematic very clean. Why do you mention the =0 sintax? D does not have it. And I do not think this problem is related to STL. What are good examples that show Tango is a good example of designing with a new D mindset? Thank you. Dee Girl
May 10 2008
next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from Dee Girl (deegirl noreply.com)'s article
 What are good examples that show Tango is a good example of designing with a
new D mindset?

I'm somewhat biased since I created the module, but I'd consider tango.core.Array to be a good example of a D-oriented mindset. It's an array-specific algorithm module intended to leverage the D slice syntax for range speficication. For exmaple: import tango.core.Array; import tango.stdc.stdio; void main() { int[] buf = [1,6,2,5,9,2,3,2,4].dup; // calls Array.sort with optional predicate buf[0 .. 3].sort( (int x, int y) { reuturn x < y; } ); assert( buf[0 .. 3] == [1,2,5,6]); buf.sort(); // full sort of buf with default predicate // below is equivalent to equal_range in C++ printf( "there are %d 2s in buf\n", buf[buf.lbound(2) .. buf.ubound(2)].length ); // more fun stuff printf( "there are %d 5s between index 2 and 6\n", buf[2 .. 6].count( 5 ) ); } etc. (I'm using printf for the sake of illustration, not because I suggest you actually use it in your app) Sean
May 10 2008
parent reply Dee Girl <deegirl noreply.com> writes:
Sean Kelly Wrote:

 == Quote from Dee Girl (deegirl noreply.com)'s article
 What are good examples that show Tango is a good example of designing with a
new D mindset?

I'm somewhat biased since I created the module, but I'd consider tango.core.Array to be a good example of a D-oriented mindset. It's an array-specific algorithm module intended to leverage the D slice syntax for range speficication. For exmaple: import tango.core.Array; import tango.stdc.stdio; void main() { int[] buf = [1,6,2,5,9,2,3,2,4].dup; // calls Array.sort with optional predicate buf[0 .. 3].sort( (int x, int y) { reuturn x < y; } ); assert( buf[0 .. 3] == [1,2,5,6]); buf.sort(); // full sort of buf with default predicate // below is equivalent to equal_range in C++ printf( "there are %d 2s in buf\n", buf[buf.lbound(2) .. buf.ubound(2)].length ); // more fun stuff printf( "there are %d 5s between index 2 and 6\n", buf[2 .. 6].count( 5 ) ); } etc. (I'm using printf for the sake of illustration, not because I suggest you actually use it in your app) Sean

Nice example! How did you do it? Did Tango change the compiler and added more methods to arrays? Thank you, Dee Girl
May 10 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 10/05/2008, Dee Girl <deegirl noreply.com> wrote:
 Nice example! How did you do it? Did Tango change the compiler and added more
methods to arrays? Thank you, Dee Girl

Yeah, but look what you can do with Phobos. import std.algorithm; int[] array = [ 1, 2, 3, 4 ]; // sort in descending order sort!("a > b")(array); assert(array == [ 4, 3, 2, 1 ]); // sort in ascending order sort(array); assert(array == [ 1, 2, 3, 4 ]); // sort with a delegate bool myComp(int x, int y) { return x > y; } sort!(myComp)(array); assert(array == [ 4, 3, 2, 1 ]); // Showcase stable sorting string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; sort!("toupper(a) < toupper(b)", SwapStrategy.stable)(words); assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);

here comes my personal emotional response: I hate the above code! it smells to much like C++. I really dislike the use of templates here, which is unnecessary, and even more so the use of those strings. a Much better way would be to use: sort(array, (int a, int b){ return x > y; }); I wish D would add the syntax sugar proposed by downs so that could be written as: sort(array, (int a, int b)) { return x > y; } also, you can rewrite it as: array.sort(...); which is even better IMO. -- Yigal
May 10 2008
parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Janice Caron wrote:
 On 10/05/2008, Dee Girl <deegirl noreply.com> wrote:
 Nice example! How did you do it? Did Tango change the compiler and added more
methods to arrays? Thank you, Dee Girl

Yeah, but look what you can do with Phobos. import std.algorithm; int[] array = [ 1, 2, 3, 4 ]; // sort in descending order sort!("a > b")(array); assert(array == [ 4, 3, 2, 1 ]); // sort in ascending order sort(array); assert(array == [ 1, 2, 3, 4 ]); // sort with a delegate bool myComp(int x, int y) { return x > y; } sort!(myComp)(array); assert(array == [ 4, 3, 2, 1 ]); // Showcase stable sorting string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; sort!("toupper(a) < toupper(b)", SwapStrategy.stable)(words); assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);

here comes my personal emotional response: I hate the above code! it smells to much like C++. I really dislike the use of templates here, which is unnecessary, and even more so the use of those strings. a Much better way would be to use: sort(array, (int a, int b){ return x > y; }); I wish D would add the syntax sugar proposed by downs so that could be written as: sort(array, (int a, int b)) { return x > y; } also, you can rewrite it as: array.sort(...); which is even better IMO.

I agree! Strings and aliases are much less powerful than delegates and closures because they are static and can not have state. But I am confused about something. I compiled this with phobos: sort!(function bool(int a, int b) { return a > b; })(array); There is clear difference in syntax from your examples. But my question is: what are the differences in semantics? Thank you, Dee Girl
May 10 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:
 I agree! Strings and aliases are much less powerful than delegates
 and closures because they are static and can not have state.
 But I am confused about something. I compiled this with phobos:
 
 sort!(function bool(int a, int b) { return a > b; })(array);
 
 There is clear difference in syntax from your examples. But my
 question is: what are the differences in semantics? Thank you, Dee
 Girl

I'm not sure what exactly are you asking. are you asking what's the difference between a delegate and a function? --Yigal
May 10 2008
parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Dee Girl wrote:
 I agree! Strings and aliases are much less powerful than delegates
 and closures because they are static and can not have state.
 But I am confused about something. I compiled this with phobos:
 
 sort!(function bool(int a, int b) { return a > b; })(array);
 
 There is clear difference in syntax from your examples. But my
 question is: what are the differences in semantics? Thank you, Dee
 Girl

I'm not sure what exactly are you asking. are you asking what's the difference between a delegate and a function?

Please let me rephrase. What is the difference between a function passed as an alias, and a delegate? You hate one and you like the other so the difference must be big. But I do not find it. Thank you, Dee Girl
May 10 2008
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Dee,

 Yigal Chripun Wrote:
 
 Dee Girl wrote:
 
 I agree! Strings and aliases are much less powerful than delegates
 and closures because they are static and can not have state.
 But I am confused about something. I compiled this with phobos:
 sort!(function bool(int a, int b) { return a > b; })(array);
 
 There is clear difference in syntax from your examples. But my
 question is: what are the differences in semantics? Thank you, Dee
 Girl
 

difference between a delegate and a function?

passed as an alias, and a delegate? You hate one and you like the other so the difference must be big. But I do not find it. Thank you, Dee Girl

a function is just a free function or a static class function or the like. A delegate is both a function and a context. Generally the context is another function's scope or an object or struct.
May 10 2008
parent reply Dee Girl <deegirl noreply.com> writes:
BCS Wrote:

 Reply to Dee,
 
 Yigal Chripun Wrote:
 
 Dee Girl wrote:
 
 I agree! Strings and aliases are much less powerful than delegates
 and closures because they are static and can not have state.
 But I am confused about something. I compiled this with phobos:
 sort!(function bool(int a, int b) { return a > b; })(array);
 
 There is clear difference in syntax from your examples. But my
 question is: what are the differences in semantics? Thank you, Dee
 Girl
 

difference between a delegate and a function?

passed as an alias, and a delegate? You hate one and you like the other so the difference must be big. But I do not find it. Thank you, Dee Girl

a function is just a free function or a static class function or the like. A delegate is both a function and a context. Generally the context is another function's scope or an object or struct.

Sorry I do not know how to express myself. It is frustrating ^_^. I understand what a function is. But it looks to me as when a function is passed to a template weird things happen. I have compiled and run successfully this program: #!/home/yasuko/bin/compile-launch -w import std.stdio; import std.algorithm; void main() { int[] array = [ 1, 2, 3, 4 ]; int x = 5; bool comp(int a, int b) { return a + x > b + x; } sort!(comp)(array); writeln(array); sort(array); writeln(array); test(&comp); writeln(array); } void test(bool delegate(int, int) dg) { int[] array = [ 1, 2, 3, 4 ]; sort!(dg)(array); } There are many interesting things about above program. Function comp accesses a variable in its enclosing scope therefore it can not be a "simple" function. However sort works with it. Then even more interesting. I pass the function as a delegate to another function. Here sort works again even when passed the delegate! So I see no difference between passing the delegate as a template alias or as a normal parameter. But I do not understand how the compiler can generate the code for the dynamic case. Dee Girl
May 10 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Your example does use delegates.
the difference is this:
say you have two delegates you pass to sort, for example you sort one
array in ascending order, and another in descending order. the template
solution will generate two almost identical sort functions with the only
difference that one sort instance calls internally the first delegate,
and the second instance calls the second. you get 4 functions. without
the template, you get one sort function and two delegates, so only 3
functions.
Now, when you use a string with the template, the sort will contain the
code itself inline to the sort instance so it "saves" you a function
call. IMO, the string solution is not clean ( C MACROs? )and while for
simple cases it could save you a function call it's premature
optimization IMO. Also, if I use delegates, I can't see how a template
can save that function call. so it just bloats my executable with
unneeded copies of sort.
I hate when library code forces me to this. I think the programmer knows
his code better than the library writer and should be able to choose
himself whether he needs to use a compile time solution to save function
calls or a run-time solution to prevent unnecessary executable bloat.
this optimization should be made by the programmer and _not_ the library
writer. This solution does not give me that freedom.
this solution is a classic STL piece of code, BUT, the main difference
is that for C++ this is probably the only reasonable solution as C++
doesn't have delegates. this is why when coding for D I urge people to
rethink their solution instead of just doing what is best in C++ (or any
other preferred language of the library writer). This is a prime example
why the C++ solution is wrong for D.

--Yigal
May 11 2008
next sibling parent reply "Koroskin Denis" <2korden gmail.com> writes:
On Sun, 11 May 2008 13:18:33 +0400, Janice Caron <caron800 googlemail.com>  
wrote:

 sort!("a.member > b.member")(array)

I like this one, too. It's very simple, efficent and clean.
May 11 2008
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Koroskin Denis wrote:
 On Sun, 11 May 2008 13:18:33 +0400, Janice Caron
 <caron800 googlemail.com> wrote:
 
 sort!("a.member > b.member")(array)

I like this one, too. It's very simple, efficent and clean.

In what way is that clean? How is that different from a C macro? if you have a typo: "a.member > b.membr" the compiler won't check it at the place you wrote it, since it's just a string. the compiler will probably complain about the library code instead. (I haven't tried that on actual code, though). Now there is a way to tell the compiler to validate the content of strings with q"here goes a valid D code snippet" (if i remember correctly), but that requires me to not forget that q. all in all, all those string manipulations, although powerful, are quite dangerous, IMO. 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. --Yigal
May 11 2008
next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
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
prev sibling next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Janice Caron, el 11 de mayo a las 12:53 me escribiste:
 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.

I think you can use Token Strings[1] in D2 to have better error reporting. It justs do lexical analisys, but it's something: sort!(q{a > b})(array); [1] http://www.digitalmars.com/d/2.0/lex.html -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- Señor, usted es militar? - No, no. Tiene un hermano militar? - No, no, no. Tiene algún pariente militar? - No, no, no, no. Tiene algún amigo íntimo militar? - No, no, pero por qué me lo pregunta? Porque me está pisando el pie. -- Tato vs. Tato (1980, Gobierno de Videla)
May 12 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Leandro Lucarella wrote:
 Janice Caron, el 11 de mayo a las 12:53 me escribiste:
 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.

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.

I think you can use Token Strings[1] in D2 to have better error reporting. It justs do lexical analisys, but it's something: sort!(q{a > b})(array); [1] http://www.digitalmars.com/d/2.0/lex.html

don't like the use of strings here at all. in any form. D is a strongly typed language which I like and all those string tricks although cool are bad IMO. my issues with the use of this syntax: a) what if I had a typo? what if i typed n instead of b (they are next to each other on my keyboard)? your solution or any other string solution cannot check this, since it is syntactically valid D code inside the string. b) if I want to compare q{a.member > b.member} there is no check that member exists, or any other type checking. the error would occur in the template code itself not in the string in both of the above examples. c) what if I forgot to type the "q"? d) most IDEs mark strings with a different color or something like that so I treat them automatically as data and not code. ( this is a minor issue, since IDEs could be made to work differently. but, my sub-conscience response to strings as "not code" is harder to fix) the only benefit to this is a slight performance benefit (though a good compiler could provide the same performance by inlining a function call) and a slightly shorter syntax which is a personal preference since you type less but IMHO it becomes less readable and less obvious. I prefer: sort!((int a, int b) {return a.member > b.member;})(array); this is more typing but provides all the checks and is more readable. Think of the poor guy that will need to do maintenance on your code years from now. now the string is a cool quick'n'dirty solution to the problem at hand but in the future that other programmer will have no idea what those "a" and "b" are. especially since you didn't write the types of them. also the performance could be the same if I used a function instead of a delegate allowing the compiler to inline it (hopefully). since this is identical to: sort(array, (int a, int b) {return a.member > b.member;}); there is no benefit in making it a template, IMO. side note: using a feature of arrays in D this could be also rewritten as: array.sort((int a, int b) {return a.member > b.member;}); Again, this is only my opinion (and I do prefer higher level solutions and think the compler/linker can do a better job optimizing than I can). you do not have to agree with this view, and there are already people that disagree.
May 12 2008
next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 my issues with the use of this syntax:
 a) what if I had a typo?

algorithm.d(2528): Error: binaryFun(ElementType1,ElementType2) is not an lvalue algorithm.d(2528): static assert "void*" Maybe the static assert message should be better. What if it says: Your comparison function is invalid?
 what if i typed n instead of b (they are next
 to each other on my keyboard)?

Same message is written.
 your solution or any other string
 solution cannot check this, since it is syntactically valid D code
 inside the string.
 b) if I want to compare q{a.member > b.member} there is no check that
 member exists, or any other type checking. the error would occur in the
 template code itself not in the string in both of the above examples.

Same message is written again.
 c) what if I forgot to type the "q"?

./test.d(9): found '}' when expecting ';' following 'statement' At least now it is in your file ^_^
 d) most IDEs mark strings with a different color or something like that
 so I treat them automatically as data and not code. ( this is a minor
 issue, since IDEs could be made to work differently. but, my
 sub-conscience response to strings as "not code" is harder to fix)

The strings are short otherwise they anyway are better as functions. I do not think this could be a problem.
 the only benefit to this is a slight performance benefit (though a good
 compiler could provide the same performance by inlining a function call)
 and a slightly shorter syntax which is a personal preference since you
 type less but IMHO it becomes less readable and less obvious.

I think it is very readable and obvious for reasons below.
 I prefer:
 sort!((int a, int b) {return a.member > b.member;})(array);
 this is more typing but provides all the checks and is more readable.

It is nice you can do both. You can put a string when it is small. You double the size of the code to write. When you have long comparison, you make it separate function. What I think is best of D when coming from other languages is that it gives so much options in such small core.
 Think of the poor guy that will need to do maintenance on your code
 years from now. now the string is a cool quick'n'dirty solution to the
 problem at hand but in the future that other programmer will have no
 idea what those "a" and "b" are. especially since you didn't write the
 types of them.

Here you may be wrong two times. First I tell from experience. I worked internship for a biotech company in Boston. I thought it will be research but it was so much work! ^_^ They used Perl and I did not know Perl when I began. But then I learned it and it has many nice things. One is sort which looks like this sort { $a <=> $b } array; I remember: first time I saw sort I understood how it works. The only odd thing was <=> but that means -1, 0 or 1 if a is less, equal or greater than b. I thought Perl has the most elegant definition of sort but now I think D has it. Second you may be wrong because you think types help. They do not help in fact they may un-help. Write this long array[] = ...; sort!((int a, int b) {return a > b;})(array); Sadly D lets long -> int conversion go through. Code will compile and run! But it gives wrong results for big numbers. For sort type deduction is best. You should write sort!((auto a, auto b) {return a > b;})(array); And that works with int, long, float, string and all types that have >. It is the best writing of the intent of the sort. But I do not know how much type inference the D compiler can do.
 also the performance could be the same if I used a function instead of a
 delegate allowing the compiler to inline it (hopefully).

Good abstraction is the best. Performance is a good bonus ^_^
 since this is identical to:
 sort(array, (int a, int b) {return a.member > b.member;});
 there is no benefit in making it a template, IMO.

I hoped I convinced you otherwise. After all effort still no understanding? Templates are best. They give you static and dynamic. Dynamic only gives dynamic.
 side note: using a feature of arrays in D this could be also rewritten as:
 array.sort((int a, int b) {return a.member > b.member;});

Would be nice to allow array.sort!(cmp).
 Again, this is only my opinion (and I do prefer higher level solutions
 and think the compler/linker can do a better job optimizing than I can).
 you do not have to agree with this view, and there are already people
 that disagree.

Different views of same issues is good. But I see there are some things about which you are simply wrong because you do not understand how they work. Then I waste time explaining ^_^ Thank you, Dee Girl
May 12 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Please read my response to Bill.
Dee Girl wrote:
 Yigal Chripun Wrote:
 
 my issues with the use of this syntax: a) what if I had a typo?

algorithm.d(2528): Error: binaryFun(ElementType1,ElementType2) is not an lvalue algorithm.d(2528): static assert "void*" Maybe the static assert message should be better. What if it says: Your comparison function is invalid?

But it doesn't. also, the file name and line number are wrong. that IS one of my problems with this.
 
 what if i typed n instead of b (they are next to each other on my
 keyboard)?

Same message is written.

again, The error I get points me to the wrong place with the wrong error.
 
 your solution or any other string solution cannot check this, since
 it is syntactically valid D code inside the string. b) if I want to
 compare q{a.member > b.member} there is no check that member
 exists, or any other type checking. the error would occur in the 
 template code itself not in the string in both of the above
 examples.

Same message is written again.

should I repeat the above, again?
 
 c) what if I forgot to type the "q"?

../test.d(9): found '}' when expecting ';' following 'statement' At least now it is in your file ^_^

oh, god! finally! (that's my inner atheist talking ;) ) and yet, I still need to type that q which is meaningless to me.
 
 d) most IDEs mark strings with a different color or something like
 that so I treat them automatically as data and not code. ( this is
 a minor issue, since IDEs could be made to work differently. but,
 my sub-conscience response to strings as "not code" is harder to
 fix)

The strings are short otherwise they anyway are better as functions. I do not think this could be a problem.
 the only benefit to this is a slight performance benefit (though a
 good compiler could provide the same performance by inlining a
 function call) and a slightly shorter syntax which is a personal
 preference since you type less but IMHO it becomes less readable
 and less obvious.

I think it is very readable and obvious for reasons below.
 I prefer: sort!((int a, int b) {return a.member >
 b.member;})(array); this is more typing but provides all the checks
 and is more readable.

It is nice you can do both. You can put a string when it is small. You double the size of the code to write. When you have long comparison, you make it separate function. What I think is best of D when coming from other languages is that it gives so much options in such small core.

code is read 100 times more then it's written you need to type more "once" but I get to read it many times.
 
 Think of the poor guy that will need to do maintenance on your code
  years from now. now the string is a cool quick'n'dirty solution to
 the problem at hand but in the future that other programmer will
 have no idea what those "a" and "b" are. especially since you
 didn't write the types of them.

Here you may be wrong two times. First I tell from experience. I worked internship for a biotech company in Boston. I thought it will be research but it was so much work! ^_^ They used Perl and I did not know Perl when I began. But then I learned it and it has many nice things. One is sort which looks like this sort { $a <=> $b } array; I remember: first time I saw sort I understood how it works. The only odd thing was <=> but that means -1, 0 or 1 if a is less, equal or greater than b. I thought Perl has the most elegant definition of sort but now I think D has it.

personally, I dislike Perl. You think that the above example is elegant?! in what way does the above make it obvious that array is sorted in ascending order?? <=> look to me as the mathematical sign for "if and only if" (iff, i think is the short form) which doesn't mean anything regarding sort order. in what way does it make clear that the sort here is a function call applied to array? this is the opposite of readable to me.
 
 Second you may be wrong because you think types help. They do not
 help in fact they may un-help. Write this
 
 long array[] = ...; sort!((int a, int b) {return a > b;})(array);
 
 Sadly D lets long -> int conversion go through. Code will compile and
 run! But it gives wrong results for big numbers.
 

fixed. the only thing your example here shows is that DMD has bugs. on the other hand I can give simple example where types do help: class A { int member();} class B { int member(); int value;} sort!(q{a.member > b.member})(array); if i change element type from A to B the string will compile. what if those classes are unrelated and the member functions do completely different things, that's just a coincidence they have the same name. when i changed to B i wanted to sort by value instead. but i could miss one of the calls to sort and the compiler will not yell at me with the string solution, thus i've introduced a search-and replace bug (i didn't replace all the instances, by mistake) a proper function will produce an error since the type of the elements is not the same at the elements in the array.
 For sort type deduction is best. You should write
 
 sort!((auto a, auto b) {return a > b;})(array);
 
 And that works with int, long, float, string and all types that have
. It is the best writing of the intent of the sort. But I do not


implement this, but it worth discussing in a different thread. also, you can define a templated delegate: bool comp!(T)(T a, T b) {return a > b;} sort!(comp)(array); however, I'd imagine that those either need to be already provided in the standard library,or there should be a default way for sorting: so, for regular ascending sort: array.sort; // no need to provide comperator. actually, I'd want sort to have another parameter to define ascending/descending sort (bool or enum or something like that). the idea is making the common case the simplest so if you just need a regular sort, just call array.sort and it'll do the right thing. you only need to actually provide a comperator if you're not doing a regular sort. and in that case, I'd prefer a function/delegate so the compiler could check that my class does have the members I'm comparing and give me the right error.
 
 also the performance could be the same if I used a function instead
 of a delegate allowing the compiler to inline it (hopefully).

Good abstraction is the best. Performance is a good bonus ^_^

Agreed. "first make sure the program is correct, then make sure it's fast"
 since this is identical to: sort(array, (int a, int b) {return
 a.member > b.member;}); there is no benefit in making it a
 template, IMO.

I hoped I convinced you otherwise. After all effort still no understanding? Templates are best. They give you static and dynamic. Dynamic only gives dynamic.

take a delegate, and you'll see that both solutions are identical. if you want static, provide a function instead of a delegate. It's a standard practice to overload functions and provide a version for both. (or wrapping it in a new type that handles this internally). there is a function call here in any case, template or no template. the compiler can inline a static function call if it's suitable (short). this is possible in both ways.
 
 side note: using a feature of arrays in D this could be also
 rewritten as: array.sort((int a, int b) {return a.member >
 b.member;});

Would be nice to allow array.sort!(cmp).
 Again, this is only my opinion (and I do prefer higher level
 solutions and think the compler/linker can do a better job
 optimizing than I can). you do not have to agree with this view,
 and there are already people that disagree.

Different views of same issues is good. But I see there are some things about which you are simply wrong because you do not understand how they work. Then I waste time explaining ^_^ Thank you, Dee Girl

English speakers) I'll ignore the implied insult in that you waste time on this discussion with me (you choose to answer, you know...). I try not to insult people, please try and do the same. besides, had it occurred to you that maybe, you didn't understand what I was saying, either because of my explanation which is not clear enough, or cause you didn't understand it? there are two things we discuss here, one of which is personal preference regarding syntax. In what way am I wrong that I like typing one and not the other? it's not a right vs. wrong issue but a Yigal's preferred style vs Janice's and Dee's preferred style. I prefer mine because I like to think it's more consistent from an end-users point of view. even if it is indeed implemented with templates under the hood. with my syntax, I just use array, then a dot and than the behavior I want to get. i.e. - array.sort; //same as array.sort(ASC); array.sort(DESC); array.sort(someStaticComperator); array.sort(aDelegateComperator); array.someOtherfunction(params); //and also List.sort; someOtherCollection.sort; int.sort; //compile time error, no sort method for int etc... I know my examples are lame (Janice is much better than me in explaining stuff). I hope you see now, what I mean by a consistent API. this is what I think the end user would prefer. what is wrong with the above, in your opinion?
May 12 2008
parent Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Please read my response to Bill.
 Dee Girl wrote:
 Yigal Chripun Wrote:
 
 my issues with the use of this syntax: a) what if I had a typo?

algorithm.d(2528): Error: binaryFun(ElementType1,ElementType2) is not an lvalue algorithm.d(2528): static assert "void*" Maybe the static assert message should be better. What if it says: Your comparison function is invalid?

But it doesn't. also, the file name and line number are wrong. that IS one of my problems with this.

Indeed it is not nice. Like in C++. But I learned to be happy with only an error. I thought errors in C++ looked bad but when I used Haskell I think C++ errors are easy ^_^
 It is nice you can do both. You can put a string when it is small.
 You double the size of the code to write. When you have long
 comparison, you make it separate function. What I think is best of D
 when coming from other languages is that it gives so much options in
 such small core.

code is read 100 times more then it's written you need to type more "once" but I get to read it many times.

This argument may be not correct. It is true code is read many times but if it is long then it is hard to read not only to write. In software engineering class I learned metrics that bug rate is proportional to code size in many languages.
 Think of the poor guy that will need to do maintenance on your code
  years from now. now the string is a cool quick'n'dirty solution to
 the problem at hand but in the future that other programmer will
 have no idea what those "a" and "b" are. especially since you
 didn't write the types of them.

Here you may be wrong two times. First I tell from experience. I worked internship for a biotech company in Boston. I thought it will be research but it was so much work! ^_^ They used Perl and I did not know Perl when I began. But then I learned it and it has many nice things. One is sort which looks like this sort { $a <=> $b } array; I remember: first time I saw sort I understood how it works. The only odd thing was <=> but that means -1, 0 or 1 if a is less, equal or greater than b. I thought Perl has the most elegant definition of sort but now I think D has it.

personally, I dislike Perl. You think that the above example is elegant?! in what way does the above make it obvious that array is sorted in ascending order?? <=> look to me as the mathematical sign for "if and only if" (iff, i think is the short form) which doesn't mean anything regarding sort order. in what way does it make clear that the sort here is a function call applied to array? this is the opposite of readable to me.

The point I make is that I found it easy to learn in practice. It is not a hypothesis. It is a story. Maybe if we worked together at the company we would both easily learned sort in perl.
 Second you may be wrong because you think types help. They do not
 help in fact they may un-help. Write this
 
 long array[] = ...; sort!((int a, int b) {return a > b;})(array);
 
 Sadly D lets long -> int conversion go through. Code will compile and
 run! But it gives wrong results for big numbers.
 

fixed. the only thing your example here shows is that DMD has bugs. on the other hand I can give simple example where types do help: class A { int member();} class B { int member(); int value;} sort!(q{a.member > b.member})(array); if i change element type from A to B the string will compile. what if those classes are unrelated and the member functions do completely different things, that's just a coincidence they have the same name. when i changed to B i wanted to sort by value instead. but i could miss one of the calls to sort and the compiler will not yell at me with the string solution, thus i've introduced a search-and replace bug (i didn't replace all the instances, by mistake) a proper function will produce an error since the type of the elements is not the same at the elements in the array.

I understand what you say. Then you have option to specify type if you want. But some other times (maybe more often) you prefer generic.
 For sort type deduction is best. You should write
 
 sort!((auto a, auto b) {return a > b;})(array);
 
 And that works with int, long, float, string and all types that have
. It is the best writing of the intent of the sort. But I do not


implement this, but it worth discussing in a different thread. also, you can define a templated delegate: bool comp!(T)(T a, T b) {return a > b;} sort!(comp)(array);

I looked in std.functional and this is exactly how it works ^_^
 however, I'd imagine that those either need to be already provided in
 the standard library,or there should be a default way for sorting:
 so, for regular ascending sort:
 array.sort; // no need to provide comperator.
 actually, I'd want sort to have another parameter to define
 ascending/descending sort (bool or enum or something like that).
 the idea is making the common case the simplest so if you just need a
 regular sort, just call array.sort and it'll do the right thing.

But this works with std.algorithm: sort(array). If you want to say array.sort is much better I think becomes pity (pety?) meaning minor issue. But you can not say you hate one and love the other just because this detail. And I see sort was wrote so that it is generic and works with others not only arrays. STL style. Then if another container appears then std.sort works with it. But if sort is forced to work only for arrays then it need many implementations (example for deque). If that works we can say std.sort is objectivally superior.
 you
 only need to actually provide a comperator if you're not doing a regular
  sort. and in that case, I'd prefer a function/delegate so the compiler
 could check that my class does have the members I'm comparing and give
 me the right error.

You can use mysort. std.sort is good because it lets you do so. If you only give me mysort then it does not let others do the static way.
 also the performance could be the same if I used a function instead
 of a delegate allowing the compiler to inline it (hopefully).

Good abstraction is the best. Performance is a good bonus ^_^

Agreed. "first make sure the program is correct, then make sure it's fast"
 since this is identical to: sort(array, (int a, int b) {return
 a.member > b.member;}); there is no benefit in making it a
 template, IMO.

I hoped I convinced you otherwise. After all effort still no understanding? Templates are best. They give you static and dynamic. Dynamic only gives dynamic.

take a delegate, and you'll see that both solutions are identical. if you want static, provide a function instead of a delegate. It's a standard practice to overload functions and provide a version for both. (or wrapping it in a new type that handles this internally). there is a function call here in any case, template or no template. the compiler can inline a static function call if it's suitable (short). this is possible in both ways.

I do not understand. Can you please write a bit of code that would be better than std.sort?
 side note: using a feature of arrays in D this could be also
 rewritten as: array.sort((int a, int b) {return a.member >
 b.member;});

Would be nice to allow array.sort!(cmp).
 Again, this is only my opinion (and I do prefer higher level



 solutions and think the compler/linker can do a better job
 optimizing than I can). you do not have to agree with this view,
 and there are already people that disagree.

Different views of same issues is good. But I see there are some things about which you are simply wrong because you do not understand how they work. Then I waste time explaining ^_^ Thank you, Dee Girl

English speakers) I'll ignore the implied insult in that you waste time on this discussion with me (you choose to answer, you know...). I try not to insult people, please try and do the same.

I am so sorry!! I used a wrong word. It was meant as spending time on explaining instead of working on my project. Sorry!
 besides, had it occurred to you that maybe, you didn't understand what I
 was saying, either because of my explanation which is not clear enough,
 or cause you didn't understand it?

It is always possible. Preferences are always not to contradict about so I never contradict about them. But some times I see a sentence or two that I think is wrong. Then I try to explain how it can be corrected. I do not know if the thinking is wrong, but if the sentence is wrong then it can be corrected.
 there are two things we discuss here, one of which is personal
 preference regarding syntax. In what way am I wrong that I like typing
 one and not the other? it's not a right vs. wrong issue but a Yigal's
 preferred style vs Janice's and Dee's preferred style.

Yes. I never wanted to say your preference is wrong. Please forgive me if I did. In things of preference maybe we agree on this. If there is serious advantage to a library then we can agree that we can accept both array.sort or sort(array).
 I prefer mine because I like to think it's more consistent from an
 end-users point of view. even if it is indeed implemented with templates
 under the hood.
 with my syntax, I just use array, then a dot and than the behavior I
 want to get.
 i.e. -
 array.sort; //same as array.sort(ASC);

Here I may prefer array.sort("a < b") instead of array.sort(ASC). Maybe I think ASC means ASCII. But if I see the comparison it is clear instantly. But it is just preference.
 array.sort(DESC);

Is DESC a shortcut for DESCRIPTION? ^_-
 array.sort(someStaticComperator);
 array.sort(aDelegateComperator);
 array.someOtherfunction(params);
 //and also
 List.sort;
 someOtherCollection.sort;
 int.sort; //compile time error, no sort method for int
 etc...

But now again we seem out of preference domain. Maybe I misunderstand but each collection implements its own sort? Then this solution is objectivally inferior because deque.sort and array.sort are duplicated. I think STL style is superior. It implements sort for anything that has iterator and swap.
 I know my examples are lame (Janice is much better than me in explaining
 stuff). I hope you see now, what I mean by a consistent API.
 this is what I think the end user would prefer.
 what is wrong with the above, in your opinion?

Subjective preferences are always good. May be not if they lead to bad design. Then if you only have a syntax preference as argument then it is too little power. If you write a design that is better than std.sort then it is perfect. I am very curious. About examples. My adviser (software engineering) tells me: if you can not show good examples for your idea then your idea has a problem. Good ideas have good examples. Thank you, Dee Girl
May 12 2008
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Yigal Chripun wrote:
 Leandro Lucarella wrote:
 Janice Caron, el 11 de mayo a las 12:53 me escribiste:
 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.

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.

It justs do lexical analisys, but it's something: sort!(q{a > b})(array); [1] http://www.digitalmars.com/d/2.0/lex.html

don't like the use of strings here at all. in any form. D is a strongly typed language which I like and all those string tricks although cool are bad IMO. my issues with the use of this syntax: a) what if I had a typo? what if i typed n instead of b (they are next to each other on my keyboard)? your solution or any other string solution cannot check this, since it is syntactically valid D code inside the string. b) if I want to compare q{a.member > b.member} there is no check that member exists, or any other type checking. the error would occur in the template code itself not in the string in both of the above examples. c) what if I forgot to type the "q"?

All of these things will cause compile-time errors. You are right that the error reported may not point to the actual mistake in a very clear way, but you will get a syntax error right away. The string gets turned into code via mixin(the_string) so if anything is not right with the code in the string, the compiler will barf. Maybe you were thinking it was doing some sort of parsing of the string to generate a comparison function for you? No. Just a raw mixin() in a scope in which 'a' and 'b' have been aliased to be the two elements being compared.
 d) most IDEs mark strings with a different color or something like that
 so I treat them automatically as data and not code. ( this is a minor
 issue, since IDEs could be made to work differently. but, my
 sub-conscience response to strings as "not code" is harder to fix)

The q{foo} thing was taken from Perl, so any IDE that can highlight perl should be able to highlight this in D with some tweaks. --bb
May 12 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Bill Baxter wrote:
 Yigal Chripun wrote:
 Leandro Lucarella wrote:
 Janice Caron, el 11 de mayo a las 12:53 me escribiste:
 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.

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.

reporting. It justs do lexical analisys, but it's something: sort!(q{a > b})(array); [1] http://www.digitalmars.com/d/2.0/lex.html

don't like the use of strings here at all. in any form. D is a strongly typed language which I like and all those string tricks although cool are bad IMO. my issues with the use of this syntax: a) what if I had a typo? what if i typed n instead of b (they are next to each other on my keyboard)? your solution or any other string solution cannot check this, since it is syntactically valid D code inside the string. b) if I want to compare q{a.member > b.member} there is no check that member exists, or any other type checking. the error would occur in the template code itself not in the string in both of the above examples. c) what if I forgot to type the "q"?

All of these things will cause compile-time errors. You are right that the error reported may not point to the actual mistake in a very clear way, but you will get a syntax error right away. The string gets turned into code via mixin(the_string) so if anything is not right with the code in the string, the compiler will barf. Maybe you were thinking it was doing some sort of parsing of the string to generate a comparison function for you? No. Just a raw mixin() in a scope in which 'a' and 'b' have been aliased to be the two elements being compared.
 d) most IDEs mark strings with a different color or something like that
 so I treat them automatically as data and not code. ( this is a minor
 issue, since IDEs could be made to work differently. but, my
 sub-conscience response to strings as "not code" is harder to fix)

The q{foo} thing was taken from Perl, so any IDE that can highlight perl should be able to highlight this in D with some tweaks. --bb

i know it's a string mixin. my personal issue with it is indeed the wrong errors. I also dislike perl. I like Smalltalk (and Ruby) though. also I'd prefer an array.sort(comp); over sort!(comp)(array); but, AGAIN, that's just MY personal preference (since I like the code blocks of Ruby/Smalltalk). it's simpler from the end user's POV.
May 12 2008
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 12/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  I prefer:
  sort!((int a, int b) {return a.member > b.member;})(array);

ints don't have a member variable called .member, so presumably, you must have meant something like class C { int member; } C[] array; sort!(C a, C b){return a.member > b.member;})(array);

 
  this is ... more readable.

No it isn't. ;-)
  but in the future that other programmer will have no
  idea what those "a" and "b" are. especially since you didn't write the
  types of them.

Incorrect. "a" and "b" are the placeholders for the things being compared. This is well documented in the documentation for sort. The only way anyone could not know what the "a" and "b" were would be if they hadn't RTFM. The type of a is the type of the array element. This is obvious.

code. documented is not the same as compiler checked, and we both know that documentation could get out of sync with the code. also, why do I have to go and search the docs if the code is in front of me? at my work we have a very large code base in Fortran, which has to be linked to every program. I don't know Fortran and don't need to since I use c++. all those things there that where so obvious to the Fortran coders 15 years ago look to me like raw binary code. they've used various low-level tricks that no one understands and that aren't properly documented. if I get a linking error with those Fortran objects I could spend days trying to figure it out. I can look at the code and try to understand what's going on, but I don't know Fortran so every bit of readability helps. replace Fortran with D in this (real life) example and try to understand that it's not so easy as you make it.
 
 
  there is no benefit in making it a template, IMO.

The benefit is choice.

choice of what? You've said so yourself, both templated and non-templated versions of the sort-with-delegate are identical. you want those string templates (which i don't like) than fine, define them as templates and i won't use them. but the version with the delegate is needlessly a template. I guess I wouldn't object as much if D's syntax for calling templated code weren't different from regular functions which would bring a more uniform syntax (take a look at macros in Nemerle). Does it irritate only me? maybe I am the odd one here. I just don't want to care if one array function is a generic template, while another isn't. ideally (not possible in current D) both forms should have the same syntax, so the end user (me) doesn't need to know nor care about what sort of implementation was chosen.
May 12 2008
next sibling parent lurker <lurker lurk.com> writes:
Somebody please throw Yigal's towel before Janice and the karate girl beat him
into a pulp. Going by his responses he has an eye closed already.
May 12 2008
prev sibling next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 The benefit is choice.

choice of what? You've said so yourself, both templated and non-templated versions of the sort-with-delegate are identical.

Yigal-san this is incorrect again. I am sorry!! Did you do the disassembly I sent you? It is very simple to change to use alias (not string). You can see how sort makes direct call to the alias, not indirect! It is always good to test first and write after.
 you want those string templates (which i don't like) than fine, define
 them as templates and i won't use them. but the version with the
 delegate is needlessly a template.
 
 I guess I wouldn't object as much if D's syntax for calling templated
 code weren't different from regular functions which would bring a more
 uniform syntax (take a look at macros in Nemerle).
 Does it irritate only me? maybe I am the odd one here. I just don't want
 to care if one array function is a generic template, while another
 isn't. ideally (not possible in current D) both forms should have the
 same syntax, so the end user (me) doesn't need to know nor care about
 what sort of implementation was chosen.

There are many things you write and then write is your opinion and others can disagree. It is good when opinion is about subjective things. And opinions are equal on subjective things like the syntax you write about. But some times you write just wrong things and do not want to try and see what is true. It does not work to say it is an opinion and others can disagree. Wrong is not an opinion. Wrong and right are not equal. It is good to try find the truth, not write wrong things as opinions. If you want truth take the disassembly and see. Thank you, Dee Girl
May 12 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:
<snip>
maybe I still didn't explain myself properly. I've used the term
"sort-with-delegates" meaning a specific instance of sort when given a
delegate as input. I thought this was clear enough, otherwise I would
have simply said "sort".
before you start sending me assembly code, let's go over it again and
make sure we both understand each other:
you take the sort template, stick in a delegate, mix for five minutes
let is simmer for one more and viola: you got the exact same thing if
you would have written sort as a regular function which receives a
delegate parameter.
the template takes the symbol it receives (the delegate name) and
"embeds" it in the sort instance.
both ways you get:
1) a call to sort
2) a call to the delegate
the only difference is the number of parameters the sort function
receives when called.
the regular sort function can run different delegates at runtime (this
is what you call "dynamic". the template has the call to the specific
delegate embedded so for different delegates you need different sort
instances.
May 12 2008
next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Dee Girl wrote:
 <snip>
 maybe I still didn't explain myself properly. I've used the term
 "sort-with-delegates" meaning a specific instance of sort when given a
 delegate as input. I thought this was clear enough, otherwise I would
 have simply said "sort".
 before you start sending me assembly code, let's go over it again and
 make sure we both understand each other:
 you take the sort template, stick in a delegate, mix for five minutes
 let is simmer for one more and viola: you got the exact same thing if
 you would have written sort as a regular function which receives a
 delegate parameter.
 the template takes the symbol it receives (the delegate name) and
 "embeds" it in the sort instance.
 both ways you get:
 1) a call to sort
 2) a call to the delegate
 the only difference is the number of parameters the sort function
 receives when called.
 the regular sort function can run different delegates at runtime (this
 is what you call "dynamic". the template has the call to the specific
 delegate embedded so for different delegates you need different sort
 instances.

Good. I think I understand. I want to be very sure, so I have a question from this code. int array[] = [1, 2, 3]; int x = 5; sort!((int a, int b) { return a + x < b + x; })(array); Two questions not one ^_^ 1. Is the code inside sort!() as powerful as delegate? 2. Is the call inside sort direct or indirect? Thank you, Dee Girl
May 12 2008
next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from Dee Girl (deegirl noreply.com)'s article
 Yigal Chripun Wrote:
 Dee Girl wrote:
 <snip>
 maybe I still didn't explain myself properly. I've used the term
 "sort-with-delegates" meaning a specific instance of sort when given a
 delegate as input. I thought this was clear enough, otherwise I would
 have simply said "sort".
 before you start sending me assembly code, let's go over it again and
 make sure we both understand each other:
 you take the sort template, stick in a delegate, mix for five minutes
 let is simmer for one more and viola: you got the exact same thing if
 you would have written sort as a regular function which receives a
 delegate parameter.
 the template takes the symbol it receives (the delegate name) and
 "embeds" it in the sort instance.
 both ways you get:
 1) a call to sort
 2) a call to the delegate
 the only difference is the number of parameters the sort function
 receives when called.
 the regular sort function can run different delegates at runtime (this
 is what you call "dynamic". the template has the call to the specific
 delegate embedded so for different delegates you need different sort
 instances.

int array[] = [1, 2, 3]; int x = 5; sort!((int a, int b) { return a + x < b + x; })(array); Two questions not one ^_^ 1. Is the code inside sort!() as powerful as delegate?

It depends what you mean by "powerful." Passing a comparator as a template parameter, as with sort!(), means that the choice of comparator must be made at compile-time rather than run-time. This may be problematic in some instances.
 2. Is the call inside sort direct or indirect? Thank you, Dee Girl

Do you mean inline or not inline? It's hard to say. DMD's inlining mechanism is a bit quirky, so it could go either way. For example, I've had struct member functions which would be inlined if static and not inlined if not static, and other member functions which were the reverse. I haven't found any way to be sure other than trying it out and examining the ASM from the object file. Sean
May 12 2008
next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Sean Kelly Wrote:

 == Quote from Dee Girl (deegirl noreply.com)'s article
 Yigal Chripun Wrote:
 Dee Girl wrote:
 <snip>
 maybe I still didn't explain myself properly. I've used the term
 "sort-with-delegates" meaning a specific instance of sort when given a
 delegate as input. I thought this was clear enough, otherwise I would
 have simply said "sort".
 before you start sending me assembly code, let's go over it again and
 make sure we both understand each other:
 you take the sort template, stick in a delegate, mix for five minutes
 let is simmer for one more and viola: you got the exact same thing if
 you would have written sort as a regular function which receives a
 delegate parameter.
 the template takes the symbol it receives (the delegate name) and
 "embeds" it in the sort instance.
 both ways you get:
 1) a call to sort
 2) a call to the delegate
 the only difference is the number of parameters the sort function
 receives when called.
 the regular sort function can run different delegates at runtime (this
 is what you call "dynamic". the template has the call to the specific
 delegate embedded so for different delegates you need different sort
 instances.

int array[] = [1, 2, 3]; int x = 5; sort!((int a, int b) { return a + x < b + x; })(array); Two questions not one ^_^ 1. Is the code inside sort!() as powerful as delegate?

It depends what you mean by "powerful." Passing a comparator as a template parameter, as with sort!(), means that the choice of comparator must be made at compile-time rather than run-time. This may be problematic in some instances.

Maybe you did not read previous discussion. Choice of comparator can be made at runtime. sort!(dg) works for a dynamic delegate dg. Just try it! It was hard to believe for me. The way D compiler insantiates sort is ingenious. This makes sort very powerful because it works with both static and dynamic code at the same time! I never found that in a language.
 2. Is the call inside sort direct or indirect? Thank you, Dee Girl

Do you mean inline or not inline? It's hard to say. DMD's inlining mechanism is a bit quirky, so it could go either way. For example, I've had struct member functions which would be inlined if static and not inlined if not static, and other member functions which were the reverse. I haven't found any way to be sure other than trying it out and examining the ASM from the object file.

Question was not about inlining. It was about indirect call with a pointer to function or a regular call. The regular call could be inlined but the pointer call is much harder. The answer is deterministic and very interesting. I think I undestand now how the D compiler generates functions from templates. Thank you, Dee Girl
May 12 2008
parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from Dee Girl (deegirl noreply.com)'s article
 Sean Kelly Wrote:
 == Quote from Dee Girl (deegirl noreply.com)'s article
 Yigal Chripun Wrote:
 Dee Girl wrote:
 <snip>
 maybe I still didn't explain myself properly. I've used the term
 "sort-with-delegates" meaning a specific instance of sort when given a
 delegate as input. I thought this was clear enough, otherwise I would
 have simply said "sort".
 before you start sending me assembly code, let's go over it again and
 make sure we both understand each other:
 you take the sort template, stick in a delegate, mix for five minutes
 let is simmer for one more and viola: you got the exact same thing if
 you would have written sort as a regular function which receives a
 delegate parameter.
 the template takes the symbol it receives (the delegate name) and
 "embeds" it in the sort instance.
 both ways you get:
 1) a call to sort
 2) a call to the delegate
 the only difference is the number of parameters the sort function
 receives when called.
 the regular sort function can run different delegates at runtime (this
 is what you call "dynamic". the template has the call to the specific
 delegate embedded so for different delegates you need different sort
 instances.

int array[] = [1, 2, 3]; int x = 5; sort!((int a, int b) { return a + x < b + x; })(array); Two questions not one ^_^ 1. Is the code inside sort!() as powerful as delegate?

It depends what you mean by "powerful." Passing a comparator as a template parameter, as with sort!(), means that the choice of comparator must be made at compile-time rather than run-time. This may be problematic in some instances.


insantiates sort is ingenious. This makes sort very powerful because it works with both static and dynamic code at the same time! I never found that in a language. So you're saying I could do this: void myfunc( bool delegate(int,int) dg ) { int[] buf = [1,2,3,4,5].dup; sort!(dg)( buf ); } I thought alias parameters had to be resolvable at compile-time. Interesting.
 2. Is the call inside sort direct or indirect? Thank you, Dee Girl

Do you mean inline or not inline? It's hard to say. DMD's inlining mechanism is a bit quirky, so it could go either way. For example, I've had struct member functions which would be inlined if static and not inlined if not static, and other member functions which were the reverse. I haven't found any way to be sure other than trying it out and examining the ASM from the object file.


very interesting. I think I undestand now how the D compiler generates functions from templates. Thank you, Dee Girl Harder but still possible. DMD doesn't do this right now, but Walter has said it may in the future. Sean
May 12 2008
next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Sean Kelly Wrote:

 == Quote from Dee Girl (deegirl noreply.com)'s article
 Sean Kelly Wrote:
 == Quote from Dee Girl (deegirl noreply.com)'s article
 Yigal Chripun Wrote:
 Dee Girl wrote:
 <snip>
 maybe I still didn't explain myself properly. I've used the term
 "sort-with-delegates" meaning a specific instance of sort when given a
 delegate as input. I thought this was clear enough, otherwise I would
 have simply said "sort".
 before you start sending me assembly code, let's go over it again and
 make sure we both understand each other:
 you take the sort template, stick in a delegate, mix for five minutes
 let is simmer for one more and viola: you got the exact same thing if
 you would have written sort as a regular function which receives a
 delegate parameter.
 the template takes the symbol it receives (the delegate name) and
 "embeds" it in the sort instance.
 both ways you get:
 1) a call to sort
 2) a call to the delegate
 the only difference is the number of parameters the sort function
 receives when called.
 the regular sort function can run different delegates at runtime (this
 is what you call "dynamic". the template has the call to the specific
 delegate embedded so for different delegates you need different sort
 instances.

int array[] = [1, 2, 3]; int x = 5; sort!((int a, int b) { return a + x < b + x; })(array); Two questions not one ^_^ 1. Is the code inside sort!() as powerful as delegate?

It depends what you mean by "powerful." Passing a comparator as a template parameter, as with sort!(), means that the choice of comparator must be made at compile-time rather than run-time. This may be problematic in some instances.


insantiates sort is ingenious. This makes sort very powerful because it works with both static and dynamic code at the same time! I never found that in a language. So you're saying I could do this: void myfunc( bool delegate(int,int) dg ) { int[] buf = [1,2,3,4,5].dup; sort!(dg)( buf ); } I thought alias parameters had to be resolvable at compile-time. Interesting.
 2. Is the call inside sort direct or indirect? Thank you, Dee Girl

Do you mean inline or not inline? It's hard to say. DMD's inlining mechanism is a bit quirky, so it could go either way. For example, I've had struct member functions which would be inlined if static and not inlined if not static, and other member functions which were the reverse. I haven't found any way to be sure other than trying it out and examining the ASM from the object file.


very interesting. I think I undestand now how the D compiler generates functions from templates. Thank you, Dee Girl Harder but still possible. DMD doesn't do this right now, but Walter has said it may in the future.

Thank you for the information. But still there is no answer to my questions. It seems nobody is interested in the actual answer. So I will keep it secret. :x Dee Girl
May 12 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:
<snip>
 1. Is the code inside sort!() as powerful as delegate?

2. Is the call inside sort direct or indirect? Thank you, Dee Girl

well, you've been answered by an expert (Thanks Sean!) I didn't know that Walter is working on making inlining delegates possible, that this is indeed great news. :) to answer your questions: as I understand it, a delegate is internally a struct with two pointers: a context pointer and a function pointer. the context pointer has the address of the outer function for nested functions, or the this pointer for methods. so, when passing a delegate you pass this struct. based on that, I think that the answer to your second question is that the call is indirect. this is true for both cases, otherwise that trick of using a sort!() instance inside a mySort function wouldn't work. all you pass to the template version is just a D symbol, in our case the name of the delegate. in both cases you need to call that delegate. you can pass regular function pointers to the template too (they are written either with the function reserved word, or with the C syntax) and those are simpler to inline. you can overload a non-templated sort function to receive those as well. I hope, some day function pointers would be implicitly castable to delegates (just make the context pointer contain null) and this will remove the need for providing two identical functions (one for delegates and one for function pointers) btw, another way of changing the delegate for a specific sort!() instance is simply to assign it; no need for a wrapper function. delegate bool(int a, int b) dg; dg = aDelegate; sort!(dg)(array); dg = otherDelegate; sort!(dg)(array); // the same instance should be used.
May 13 2008
parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Dee Girl wrote:
 <snip>
 1. Is the code inside sort!() as powerful as delegate?

2. Is the call inside sort direct or indirect? Thank you, Dee Girl

well, you've been answered by an expert (Thanks Sean!)

Definitely Sean is a excellent expert. But my understanding of his answer is a bit different. I see he did not know the answer to the first question. So I gave the answer to him, and he said it is something he did not know before. Then he missed the second question.
 I didn't know that Walter is working on making inlining delegates
 possible, that this is indeed great news. :)

I think this is great but it is much easy said than done. Inlining an indirect call means the callee must be inlined too. Or at least duplicated. There is complicated analysis that shows which indirect calls can be inlined that way. Not all can.
 to answer your questions:
 as I understand it, a delegate is internally a struct with two pointers:
 a context pointer and a function pointer. the context pointer has the
 address of the outer function for nested functions, or the this pointer
 for methods. so, when passing a delegate you pass this struct.
 based on that, I think that the answer to your second question is that
 the call is indirect.

This answer is incorrect. The call is direct. I clarify: this is not matter of optimization or tricks. It is matter of principle. It is coming from the very clever way Walter instantiates templates with alias arguments. He specializes the template at the call site. I wish I had his idea for my project but not it is too late ^_^ I think this is very important feature of the D programming language.
 this is true for both cases, otherwise that trick
 of using a sort!() instance inside a mySort function wouldn't work. all
 you pass to the template version is just a D symbol, in our case the
 name of the delegate. in both cases you need to call that delegate.

In the example I wrote it is not the name of the delegate. It is the name of nested function. It is anonymous but still has internal name.
 you can pass regular function pointers to the template too (they are
 written either with the function reserved word, or with the C syntax)
 and those are simpler to inline. you can overload a non-templated sort
 function to receive those as well.

If a pointer to function is passed then template function is instantiated with that pointer type. Discussion on when that can be inlined takes to another subject.
 I hope, some day function pointers would be implicitly castable to
 delegates (just make the context pointer contain null) and this will
 remove the need for providing two identical functions (one for delegates
 and one for function pointers)
 btw, another way of changing the delegate for a specific sort!()
 instance is simply to assign it; no need for a wrapper function.
 delegate bool(int a, int b) dg;
 dg = aDelegate;
 sort!(dg)(array);
 dg = otherDelegate;
 sort!(dg)(array); // the same instance should be used.

That is correct. The same instance will be used. Thank you, Dee Girl
May 13 2008
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:

 I didn't know that Walter is working on making inlining delegates 
 possible, that this is indeed great news. :)

I think this is great but it is much easy said than done. Inlining an indirect call means the callee must be inlined too. Or at least duplicated. There is complicated analysis that shows which indirect calls can be inlined that way. Not all can.

either way, It's the compiler/linker job to make optimizations. it sure is more capable and has more info than I am. I'm not implementing the D compiler, I'm using it. my POV is that of the user. I believe Walter is a genius compiler writer and that if it's possible he'll make it happen.
 to answer your questions: as I understand it, a delegate is
 internally a struct with two pointers: a context pointer and a
 function pointer. the context pointer has the address of the outer
 function for nested functions, or the this pointer for methods. so,
 when passing a delegate you pass this struct. based on that, I
 think that the answer to your second question is that the call is
 indirect.

This answer is incorrect. The call is direct. I clarify: this is not matter of optimization or tricks. It is matter of principle. It is coming from the very clever way Walter instantiates templates with alias arguments. He specializes the template at the call site. I wish I had his idea for my project but not it is too late ^_^ I think this is very important feature of the D programming language.

important to me, again I trust Walter to make the compiler produce the best possible code. I meant that the sort instance calls the delegate anyway and does not inline it. I'm not sure what you mean in direct vs. indirect and why this is important. I thought that the important thing for performance was whether the call gets inlined or not. I'm saying here that both options (the template instance and the regular function) do the same regarding inlining the delegate.
 
 
 this is true for both cases, otherwise that trick of using a
 sort!() instance inside a mySort function wouldn't work. all you
 pass to the template version is just a D symbol, in our case the 
 name of the delegate. in both cases you need to call that delegate.
 

In the example I wrote it is not the name of the delegate. It is the name of nested function. It is anonymous but still has internal name.

see Janice's reply. effectively this is the same thing.
 
 you can pass regular function pointers to the template too (they
 are written either with the function reserved word, or with the C
 syntax) and those are simpler to inline. you can overload a
 non-templated sort function to receive those as well.

If a pointer to function is passed then template function is instantiated with that pointer type. Discussion on when that can be inlined takes to another subject.
 I hope, some day function pointers would be implicitly castable to 
 delegates (just make the context pointer contain null) and this
 will remove the need for providing two identical functions (one for
 delegates and one for function pointers) btw, another way of
 changing the delegate for a specific sort!() instance is simply to
 assign it; no need for a wrapper function. delegate bool(int a, int
 b) dg; dg = aDelegate; sort!(dg)(array); dg = otherDelegate; 
 sort!(dg)(array); // the same instance should be used.

That is correct. The same instance will be used. Thank you, Dee Girl

May 13 2008
parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Dee Girl wrote:
 
 I didn't know that Walter is working on making inlining delegates 
 possible, that this is indeed great news. :)

I think this is great but it is much easy said than done. Inlining an indirect call means the callee must be inlined too. Or at least duplicated. There is complicated analysis that shows which indirect calls can be inlined that way. Not all can.

either way, It's the compiler/linker job to make optimizations. it sure is more capable and has more info than I am. I'm not implementing the D compiler, I'm using it. my POV is that of the user. I believe Walter is a genius compiler writer and that if it's possible he'll make it happen.

But this is the interesting thing. Walter is a genius compiler writer and he made it happen already. Now. Your compiler has it already. And it has it in very elegant form. The point I tried many times to clarify is that Walter has done the great work already. But it is not in the documents. Maybe only Walter knows it now. And Andrei because he wrote std.algorithm exactly to use the feature.
 to answer your questions: as I understand it, a delegate is
 internally a struct with two pointers: a context pointer and a
 function pointer. the context pointer has the address of the outer
 function for nested functions, or the this pointer for methods. so,
 when passing a delegate you pass this struct. based on that, I
 think that the answer to your second question is that the call is
 indirect.

This answer is incorrect. The call is direct. I clarify: this is not matter of optimization or tricks. It is matter of principle. It is coming from the very clever way Walter instantiates templates with alias arguments. He specializes the template at the call site. I wish I had his idea for my project but not it is too late ^_^ I think this is very important feature of the D programming language.


I am using standard terminology. But maybe my sentences are wrong.
 anyway, this is not really that
 important to me, again I trust Walter to make the compiler produce the
 best possible code.
 I meant that the sort instance calls the delegate anyway and does not
 inline it. I'm not sure what you mean in direct vs. indirect and why
 this is important. I thought that the important thing for performance
 was whether the call gets inlined or not.

It is important because of this. Direct call is trivially inlined. Indirect call is maybe 1000 times harder to inline and sometimes impossible. By direct call I mean you know the address of the function at compilation. By indirect call I mean you have a pointer with the address of the function.
 I'm saying here that both options (the template instance and the regular
  function) do the same regarding inlining the delegate.

This sentence is incorrect. The template instance works very different from the regular function.
 this is true for both cases, otherwise that trick of using a
 sort!() instance inside a mySort function wouldn't work. all you
 pass to the template version is just a D symbol, in our case the 
 name of the delegate. in both cases you need to call that delegate.
 

In the example I wrote it is not the name of the delegate. It is the name of nested function. It is anonymous but still has internal name.

see Janice's reply. effectively this is the same thing.

They are not the same thing. I have explained in other post. Thank you, Dee Girl
May 13 2008
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:
 It is important because of this. Direct call is trivially inlined.
 Indirect call is maybe 1000 times harder to inline and sometimes
 impossible. By direct call I mean you know the address of the
 function at compilation. By indirect call I mean you have a pointer
 with the address of the function.

again, I'll leave this to Walter to figure out. the decision to inline is and should be the compiler's.
 
 I'm saying here that both options (the template instance and the
 regular function) do the same regarding inlining the delegate.

This sentence is incorrect. The template instance works very different from the regular function.

you say that sort!(delegate) inlines the call to delegate? in that case: dg = dg1; sort!(dg)(..); dg = dg2; sort!(dg)(..); whould the above produce two different instances? with classes: class A { bool method(); } auto a = new A; auto b = new B; dg = &a.method; sort!(dg)(..); dg = &b.method; sort!(dg)(..); will this produce two instances? I understood till now that the sort!(delegate)(..) /may/ inline the delegate but it's not a must. on the other hand: sort(delegate,..) currently doesn't inline but /may/ do so in the future. in the end, both will /maybe/ provide inline capability. if this is true I don't see any difference.
May 13 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 2008/5/13 Yigal Chripun <yigal100 gmail.com>:
 <snip> Lots of stuff about inlining

Please don't confuse inlining with instantiation. This does not help the discussion. Whether or not a compiler inlines a function or not is a decision made at the call site, however the function body must still exist elsewhere in the object file, in case other functions in other modules need to call it non-inlined. If nothing calls it non-inlined, the linker should remove it when building the executable. This has nothing whatsoever to do with template instantiation. Inlining may or may not happen with either functions or delegates, but it has no bearing whatsoever on the "to template or not" discussion.

you know, it's not easy to have two parallel conversations with two different persons about almost the same thing, while one is is making one point and another makes a different point. concurrency is hard!! Dee talks about inlining, and here I completely agree with you about:
 Inlining may or may not happen with either functions or delegates,
 but it has no bearing whatsoever on the "to template or not"
 discussion.

for me the question is how a template parametrized on the entire delegate (i.e. an alias) compares with a template parametrized on the element type of the array (which is how I would have done this, I think) in regard to the number of instantiations of both solutions. I'd like to hear your explanation comparing the two. Thanks for your patience (is this spelled right?) --Yigal
May 13 2008
prev sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
Dee Girl wrote:
 Yigal Chripun Wrote:
 
 Dee Girl wrote:

 I didn't know that Walter is working on making inlining delegates 
 possible, that this is indeed great news. :)

indirect call means the callee must be inlined too. Or at least duplicated. There is complicated analysis that shows which indirect calls can be inlined that way. Not all can.

either way, It's the compiler/linker job to make optimizations. it sure is more capable and has more info than I am. I'm not implementing the D compiler, I'm using it. my POV is that of the user. I believe Walter is a genius compiler writer and that if it's possible he'll make it happen.

But this is the interesting thing. Walter is a genius compiler writer and he made it happen already. Now. Your compiler has it already. And it has it in very elegant form. The point I tried many times to clarify is that Walter has done the great work already. But it is not in the documents. Maybe only Walter knows it now. And Andrei because he wrote std.algorithm exactly to use the feature.

In fact, I think this feature was probably added specifically because Andrei needed it for std.algorithm. It has never been discussed or documented anywhere, to my knowledge. Sean
May 13 2008
parent Sean Kelly <sean invisibleduck.org> writes:
Sean Kelly wrote:
 Dee Girl wrote:
 Yigal Chripun Wrote:

 Dee Girl wrote:

 I didn't know that Walter is working on making inlining delegates 
 possible, that this is indeed great news. :)

indirect call means the callee must be inlined too. Or at least duplicated. There is complicated analysis that shows which indirect calls can be inlined that way. Not all can.

either way, It's the compiler/linker job to make optimizations. it sure is more capable and has more info than I am. I'm not implementing the D compiler, I'm using it. my POV is that of the user. I believe Walter is a genius compiler writer and that if it's possible he'll make it happen.

But this is the interesting thing. Walter is a genius compiler writer and he made it happen already. Now. Your compiler has it already. And it has it in very elegant form. The point I tried many times to clarify is that Walter has done the great work already. But it is not in the documents. Maybe only Walter knows it now. And Andrei because he wrote std.algorithm exactly to use the feature.

In fact, I think this feature was probably added specifically because Andrei needed it for std.algorithm. It has never been discussed or documented anywhere, to my knowledge.

I take it back. I created some test code for D 1.0 maybe a year ago that uses this feature, so it must have worked in D 1.0 at some point. I must be getting old--my memory is going :-p Sean
May 18 2008
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
Dee Girl wrote:
 Yigal Chripun Wrote:
 
 Dee Girl wrote:
 <snip>
 1. Is the code inside sort!() as powerful as delegate?

 2. Is the call inside sort direct or indirect? Thank you, Dee Girl


Definitely Sean is a excellent expert. But my understanding of his answer is a bit different. I see he did not know the answer to the first question. So I gave the answer to him, and he said it is something he did not know before. Then he missed the second question.

Regarding the alias stuff I didn't know before. I don't suppose you know why it doesn't work in D 1.0? It isn't documented in the 2.0 spec so either it's a new omission or it's a feature which doesn't require a spec change and should therefore be supported in D 1.0 as well. As for the second question--you're right that I didn't understand what you were asking. Given the classic D 1.0 concept of alias template parameters I'd say a direct call occurred, but for some instances (like my example), that isn't possible. So I'd say it depends on what the alias refers to, though I haven't looked at the asm to be sure.
 I didn't know that Walter is working on making inlining delegates
 possible, that this is indeed great news. :)

I think this is great but it is much easy said than done. Inlining an indirect call means the callee must be inlined too. Or at least duplicated. There is complicated analysis that shows which indirect calls can be inlined that way. Not all can.
 to answer your questions:
 as I understand it, a delegate is internally a struct with two pointers:
 a context pointer and a function pointer. the context pointer has the
 address of the outer function for nested functions, or the this pointer
 for methods. so, when passing a delegate you pass this struct.
 based on that, I think that the answer to your second question is that
 the call is indirect.

This answer is incorrect. The call is direct. I clarify: this is not matter of optimization or tricks. It is matter of principle. It is coming from the very clever way Walter instantiates templates with alias arguments. He specializes the template at the call site. I wish I had his idea for my project but not it is too late ^_^

See above. I believe it's direct when it can be. Still a bit weird that a template can be instantiated with a non-static variable, but the approach is certainly sound so who am I to complain :-) Sean
May 13 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Sean Kelly wrote:
 
 So you're saying I could do this:
 
 void myfunc( bool delegate(int,int) dg )
 {
     int[] buf = [1,2,3,4,5].dup;
     sort!(dg)( buf );
 }
 
 I thought alias parameters had to be resolvable at compile-time.  Interesting.
 

Hum, I thought the same. And frankly I couldn't see how it could work otherwise, so it took me a while, and several experimentations, to figure out how it works. Apparently, if the alias parameter is local, it is as if the template body is instantiated in the local scope where the template instance occurs, and if the template contains a function, it is as if the function used an outer local variable (and thus the function becomes a nested function). After this, didn't took me long to cook up a bug using this feature :P : http://d.puremagic.com/issues/show_bug.cgi?id=2148 -- Bruno Medeiros - Software Developer, MSc. in CS/E graduate http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 10 2008
prev sibling next sibling parent Dee Girl <deegirl noreply.com> writes:
Janice Caron Wrote:

 2008/5/13 Sean Kelly <sean invisibleduck.org>:
  It depends what you mean by "powerful."  Passing a comparator as a template
parameter,
  as with sort!(), means that the choice of comparator must be made at
compile-time rather
  than run-time.  This may be problematic in some instances.

I think you meant to say, the choice of comparator /may/ be made at compile-time. Not must. sort!(compare)(array); // compile time sort(compare, array); // run time

Even more simple. sort!(compare)(array); sort!(compare)(array); Decision depends on what compare is. Dee Girl
May 13 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Yigal Chripun <yigal100 gmail.com>:
 <snip> Lots of stuff about inlining

Please don't confuse inlining with instantiation. This does not help the discussion. Whether or not a compiler inlines a function or not is a decision made at the call site, however the function body must still exist elsewhere in the object file, in case other functions in other modules need to call it non-inlined. If nothing calls it non-inlined, the linker should remove it when building the executable. This has nothing whatsoever to do with template instantiation. Inlining may or may not happen with either functions or delegates, but it has no bearing whatsoever on the "to template or not" discussion.
May 13 2008
prev sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
Janice Caron wrote:
 2008/5/13 Sean Kelly <sean invisibleduck.org>:
  It depends what you mean by "powerful."  Passing a comparator as a template
parameter,
  as with sort!(), means that the choice of comparator must be made at
compile-time rather
  than run-time.  This may be problematic in some instances.

I think you meant to say, the choice of comparator /may/ be made at compile-time. Not must. sort!(compare)(array); // compile time sort(compare, array); // run time

I meant the way sort works in std.algorithm. But it appears that my answer was D 1.0-specific. I had no idea D 2.0 added the option to instantiate a template on an alias to a local variable. Sean
May 13 2008
parent reply Don <nospam nospam.com.au> writes:
Sean Kelly wrote:
 Janice Caron wrote:
 2008/5/13 Sean Kelly <sean invisibleduck.org>:
  It depends what you mean by "powerful."  Passing a comparator as a 
 template parameter,
  as with sort!(), means that the choice of comparator must be made at 
 compile-time rather
  than run-time.  This may be problematic in some instances.

I think you meant to say, the choice of comparator /may/ be made at compile-time. Not must. sort!(compare)(array); // compile time sort(compare, array); // run time

I meant the way sort works in std.algorithm. But it appears that my answer was D 1.0-specific. I had no idea D 2.0 added the option to instantiate a template on an alias to a local variable.

It should work in D1.0, as well. It was added not long before D1.00 came out.
May 13 2008
parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from Don (nospam nospam.com.au)'s article
 Sean Kelly wrote:
 Janice Caron wrote:
 2008/5/13 Sean Kelly <sean invisibleduck.org>:
  It depends what you mean by "powerful."  Passing a comparator as a
 template parameter,
  as with sort!(), means that the choice of comparator must be made at
 compile-time rather
  than run-time.  This may be problematic in some instances.

I think you meant to say, the choice of comparator /may/ be made at compile-time. Not must. sort!(compare)(array); // compile time sort(compare, array); // run time

I meant the way sort works in std.algorithm. But it appears that my answer was D 1.0-specific. I had no idea D 2.0 added the option to instantiate a template on an alias to a local variable.

out.

It doesn't. Or more specifically, this doesn't work in D 1.0: void sort(alias cmp, T)( T buf ) { // pretend we're sorting cmp( buf[0], buf[1] ); } void doSort( int[] buf, bool delegate(int,int) cmp ) { sort!(cmp)( buf ); } Sean
May 13 2008
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 == Quote from Don (nospam nospam.com.au)'s article
 Sean Kelly wrote:
 Janice Caron wrote:
 2008/5/13 Sean Kelly <sean invisibleduck.org>:
  It depends what you mean by "powerful."  Passing a comparator as a
 template parameter,
  as with sort!(), means that the choice of comparator must be made at
 compile-time rather
  than run-time.  This may be problematic in some instances.

compile-time. Not must. sort!(compare)(array); // compile time sort(compare, array); // run time

answer was D 1.0-specific. I had no idea D 2.0 added the option to instantiate a template on an alias to a local variable.

out.

It doesn't. Or more specifically, this doesn't work in D 1.0: void sort(alias cmp, T)( T buf ) { // pretend we're sorting cmp( buf[0], buf[1] ); } void doSort( int[] buf, bool delegate(int,int) cmp ) { sort!(cmp)( buf ); } Sean

Oh, *that's* what you guys are talking about? Here's the bugzilla bug for it: http://d.puremagic.com/issues/show_bug.cgi?id=493 --bb
May 13 2008
parent Sean Kelly <sean invisibleduck.org> writes:
Bill Baxter wrote:
 Sean Kelly wrote:
 == Quote from Don (nospam nospam.com.au)'s article
 Sean Kelly wrote:
 Janice Caron wrote:
 2008/5/13 Sean Kelly <sean invisibleduck.org>:
  It depends what you mean by "powerful."  Passing a comparator as a
 template parameter,
  as with sort!(), means that the choice of comparator must be made at
 compile-time rather
  than run-time.  This may be problematic in some instances.

compile-time. Not must. sort!(compare)(array); // compile time sort(compare, array); // run time

answer was D 1.0-specific. I had no idea D 2.0 added the option to instantiate a template on an alias to a local variable.

out.

It doesn't. Or more specifically, this doesn't work in D 1.0: void sort(alias cmp, T)( T buf ) { // pretend we're sorting cmp( buf[0], buf[1] ); } void doSort( int[] buf, bool delegate(int,int) cmp ) { sort!(cmp)( buf ); } Sean

Oh, *that's* what you guys are talking about? Here's the bugzilla bug for it: http://d.puremagic.com/issues/show_bug.cgi?id=493

And here I thought D 1.0 just didn't like my aliasing a local variable. Go figure. Sean
May 13 2008
prev sibling next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Janice Caron Wrote:

 2008/5/13 Dee Girl <deegirl noreply.com>:
  int array[] = [1, 2, 3];
  int x = 5;
  sort!((int a, int b) { return a + x < b + x; })(array);

  Two questions not one ^_^

  1. Is the code inside sort!() as powerful as delegate?

The code inside sort!() /is/ a delegate. It's an anonymous delegate, but its implementation is exactly the same as a delegate with a name. It's equivalent to int foo (int a, int b) { return a + x < b + x; }; sort!(foo)(array);

Yes, excellent point. The codes are equivalent. But foo is not a delegate. It is a nested function. Like in Pascal. I am not sure how it can become delegate. Maybe Walter can answer because I see he writes here. Maybe when somebody takes address of foo. Dee Girl
May 13 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:
 Janice Caron Wrote:
 
 2008/5/13 Dee Girl <deegirl noreply.com>:
  int array[] = [1, 2, 3];
  int x = 5;
  sort!((int a, int b) { return a + x < b + x; })(array);

  Two questions not one ^_^

  1. Is the code inside sort!() as powerful as delegate?

but its implementation is exactly the same as a delegate with a name. It's equivalent to int foo (int a, int b) { return a + x < b + x; }; sort!(foo)(array);

Yes, excellent point. The codes are equivalent. But foo is not a delegate. It is a nested function. Like in Pascal. I am not sure how it can become delegate. Maybe Walter can answer because I see he writes here. Maybe when somebody takes address of foo. Dee Girl

foo _is_ a delegate. there is no difference: As I explained in a different post, a delegate is simply two pointers where the context pointer either points to the surrounding function for nested functions or to the this pointer for methods.
May 13 2008
next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Dee Girl wrote:
 Janice Caron Wrote:
 
 2008/5/13 Dee Girl <deegirl noreply.com>:
  int array[] = [1, 2, 3];
  int x = 5;
  sort!((int a, int b) { return a + x < b + x; })(array);

  Two questions not one ^_^

  1. Is the code inside sort!() as powerful as delegate?

but its implementation is exactly the same as a delegate with a name. It's equivalent to int foo (int a, int b) { return a + x < b + x; }; sort!(foo)(array);

Yes, excellent point. The codes are equivalent. But foo is not a delegate. It is a nested function. Like in Pascal. I am not sure how it can become delegate. Maybe Walter can answer because I see he writes here. Maybe when somebody takes address of foo. Dee Girl

foo _is_ a delegate. there is no difference: As I explained in a different post, a delegate is simply two pointers where the context pointer either points to the surrounding function for nested functions or to the this pointer for methods.

It is clear what is a delegate. It is pointer to a function and pointer to environment. Like in Lisp. But foo is not delegate. It is nested function. Like in Pascal. If you call foo from the same scope there is regular function call. Not delegate call! If you take address &foo it becomes delegate. (I just checked). D has very interesting scheme. I hope Walter writes here and confirms. But your explanation is wrong. I hope you are not offended. I am sorry, Dee Girl
May 13 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:

 foo _is_ a delegate. there is no difference: As I explained in a
 different post, a delegate is simply two pointers where the context
 pointer either points to the surrounding function for nested
 functions or to the this pointer for methods.

It is clear what is a delegate. It is pointer to a function and pointer to environment. Like in Lisp.

exactly what I've said.
 
 But foo is not delegate. It is nested function. Like in Pascal. If
 you call foo from the same scope there is regular function call. Not
 delegate call! If you take address &foo it becomes delegate. (I just
 checked). D has very interesting scheme. I hope Walter writes here
 and confirms. But your explanation is wrong. I hope you are not
 offended. I am sorry, Dee Girl
 

function the same way you can pass a class method. so it's a nested function that is automagically becomes a delegate by the compiler. the functionality is the same. for simplicity sake let me call it a delegate. now that we put the terminology differences aside, we both agree about the behavior of this D feature. From what I know many languages behave exactly like this (turning nested functions into delegates). side note: I heard Walter doesn't like anything that looks or smells like pascal.
May 13 2008
parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Dee Girl wrote:
 
 foo _is_ a delegate. there is no difference: As I explained in a
 different post, a delegate is simply two pointers where the context
 pointer either points to the surrounding function for nested
 functions or to the this pointer for methods.

It is clear what is a delegate. It is pointer to a function and pointer to environment. Like in Lisp.

exactly what I've said.
 
 But foo is not delegate. It is nested function. Like in Pascal. If
 you call foo from the same scope there is regular function call. Not
 delegate call! If you take address &foo it becomes delegate. (I just
 checked). D has very interesting scheme. I hope Walter writes here
 and confirms. But your explanation is wrong. I hope you are not
 offended. I am sorry, Dee Girl
 

function the same way you can pass a class method. so it's a nested function that is automagically becomes a delegate by the compiler. the functionality is the same. for simplicity sake let me call it a delegate. now that we put the terminology differences aside, we both agree about the behavior of this D feature. From what I know many languages behave exactly like this (turning nested functions into delegates).

But no language I know can pass the nested function by alias. Pass by alias is fundamental different from pass by delegate. I have tried to explain it all this time. Do we agree on this now?
 side note: I heard Walter doesn't like anything that looks or smells
 like pascal.

Then I am sure he wish Pascal did not have nested functions ^_^ PS I installed spell check for English on emacs now, at least fewer typos! Thank you, Dee Girl
May 13 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:
 
 But no language I know can pass the nested function by alias. Pass by
 alias is fundamental different from pass by delegate. I have tried to
 explain it all this time. Do we agree on this now?

the main thing for me is the syntax: look at Nemerle, for example. Here's a snippet: class MoreFunctions { static run_twice (f : int -> int, v : int) : int { f (f (v)) } static run_adder (x : int) : void { def f (y : int) : int { x + y }; // <==== this is a nested function System.Console.WriteLine ("{0}", run_twice (f, 1)) } public static Run () : void { run_adder (1); run_adder (2); } } look at: http://nemerle.org/Grok_Functionals for a better explanation.
 
 side note: I heard Walter doesn't like anything that looks or
 smells like pascal.

Then I am sure he wish Pascal did not have nested functions ^_^ PS I installed spell check for English on emacs now, at least fewer typos! Thank you, Dee Girl

May 13 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  the main thing for me is the syntax:
  look at Nemerle, for example.
  Here's a snippet:

  class MoreFunctions {
    static run_twice (f : int -> int, v : int) : int
    {
      f (f (v))
    }

    static run_adder (x : int) : void
    {
      def f (y : int) : int { x + y }; // <==== this is a nested function
      System.Console.WriteLine ("{0}", run_twice (f, 1))
    }

    public static Run () : void
    {
      run_adder (1);
      run_adder (2);
    }
   }

And the same thing in D: class MoreFunctions { static run_twice!(alias f)(int v) { f(f(v)) } static run_adder (x : int) : void { int f(int y) { return x + y; }; writefln("{0}", run_twice!(f)(1)) } void main() { run_adder(1); run_adder(2); } } What's your point?

same functionality.
May 13 2008
parent Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Janice Caron wrote:
 2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  the main thing for me is the syntax:
  look at Nemerle, for example.
  Here's a snippet:

  class MoreFunctions {
    static run_twice (f : int -> int, v : int) : int
    {
      f (f (v))
    }

    static run_adder (x : int) : void
    {
      def f (y : int) : int { x + y }; // <==== this is a nested function
      System.Console.WriteLine ("{0}", run_twice (f, 1))
    }

    public static Run () : void
    {
      run_adder (1);
      run_adder (2);
    }
   }

And the same thing in D: class MoreFunctions { static run_twice!(alias f)(int v) { f(f(v)) } static run_adder (x : int) : void { int f(int y) { return x + y; }; writefln("{0}", run_twice!(f)(1)) } void main() { run_adder(1); run_adder(2); } } What's your point?

same functionality.

I do not know Nemerle. But the way run_twice looks it is not parameterized statically. So all calls to run_twice will pass a pointer to function and go to the same body. Many languages do it. It is very different from D run_twice which is parameterized statically. There is big difference. Also you said I talk about inlining. No. Janice is right. I talk about instantiation. I write too much but I think I can not explain things well. Maybe better I stop in this discussion. Sorry, Dee Girl
May 13 2008
prev sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 2008/5/13 Koroskin Denis <2korden gmail.com>:
 foo _is_ a delegate.

pointer-to-environment passed as a first (hidden) parameter

I'm sure Yigal intended to mean "&foo _is_ a delegate". I understood him, anyway.

:-)
May 13 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Janice Caron <caron800 googlemail.com>:
         static run_adder (x : int) : void

Darn copy/paste errors. I meant of course static void run_adder(int x)
May 13 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  the main thing for me is the syntax:
  look at Nemerle, for example.
  Here's a snippet:

  class MoreFunctions {
    static run_twice (f : int -> int, v : int) : int
    {
      f (f (v))
    }

    static run_adder (x : int) : void
    {
      def f (y : int) : int { x + y }; // <==== this is a nested function
      System.Console.WriteLine ("{0}", run_twice (f, 1))
    }

    public static Run () : void
    {
      run_adder (1);
      run_adder (2);
    }
   }

And the same thing in D: class MoreFunctions { static run_twice!(alias f)(int v) { f(f(v)) } static run_adder (x : int) : void { int f(int y) { return x + y; }; writefln("{0}", run_twice!(f)(1)) } void main() { run_adder(1); run_adder(2); } } What's your point?
May 13 2008
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 12/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  > The type of a is the type of the array element. This is obvious.

 again, obvious to you, not that dude five years from now reading your
  code. documented is not the same as compiler checked, and we both know
  that documentation could get out of sync with the code.

I'm talking about the documentation for std.algorithm.sort. What are you talking about? It is perfectly reasonable to expect that anyone using std.algorithm.sort will have read the documentation on how to use it.
  also, why do I have to go and search the docs if the code is in front of me?

You don't. You learn how to use std.algorithm.sort, and then you use it, going back to the docs only occasionally. It's the same with anything. If I want to use a std::vector in C++, I'd better have read up on what std::vectors are and how to use them first. So yes, it's obvious what the "a" and "b" mean.
 The benefit is choice.


Of everything. Of having it do whatever you want, whatever that is.
 You've said so yourself, both templated and
 non-templated versions of the sort-with-delegate are identical.

I have said no such thing! (And I take great offense at being misquoted) I may have said that *one particular instantiation* of the template is identical to the delegate version. Fair enough. But I can also make different instantiations.

 
  you want those string templates (which i don't like) than fine, define
  them as templates and i won't use them. but the version with the
  delegate is needlessly a template.

There /is/ no "version with the delegate". But you can /make/ a version with a delegate out of the template.

Again, this is exactly what I want. this should be in the standard library.
 
 And those string parameters are brilliant! Today I wanted to strip out
 all underscores from a string. So I sat down to write the function -
 it's not really hard, after all - but then, with a flash of
 inspiration, I realised the function already exists. It is:
 
     filter!("a != '_'")(s);
 
 That is just /so/ amazing.
 

 
 
  ideally (not possible in current D) both forms should have the
  same syntax, so the end user (me) doesn't need to know nor care about
  what sort of implementation was chosen.

You haven't heard of aliases or wrapper functions then?

I meant possible *directly* not via wrappers. example time: I want the following API: (I don't care now for the internal implementation and the wrapper functions/aliases should be provided by the standard library) array.sort; //same as array.sort(ASC); array.sort(DESC); array.sort(someStaticComperator); array.sort(aDelegateComperator); array.someOtherfunction(params); List.sort; someOtherCollection.sort; int.sort; //compile time error, no sort method for int etc...
May 12 2008
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  I want the following API:
  (I don't care now for the internal implementation and the wrapper
  functions/aliases should be provided by the standard library)

  array.sort; //same as array.sort(ASC);

That one already works.
  array.sort(DESC);

alias std.algorithm.sort!("b<a") sort_reverse; array.sort_reverse; Is that close enough?

I want this alias in the library. I'd prefer a sort that received some enum (also incorporating the stable vs non-stable enum constants) but I can settle for this, _if_ it were present in the library. Those are the small things that make a difference. after all D does provide a string alias too.
 
 
  array.sort(someStaticComperator);
  array.sort(aDelegateComperator);
  array.someOtherfunction(params);

That will never be possible either with or without templates. Guess why? Well, to save you guessing, I'll tell you. It's because "sort" is a built-in property of D's dynamic arrays. (Like length and ptr are built in). In general, we can define a function foo upon arrays which can be called as either foo(array,otherParams); array.foo(otherParams); But unfortunately, you can't do that if the function happens to be named "sort". (Likewise "length", "ptr", "init", and so on). As I said, this is /not/ a limitation of templates. If Walter were to remove the built-in sort property of arrays, then all of a sudden you would have: array.sort(someStaticComperator); array.sort(aDelegateComperator);

than, either the buit-in sort needs to be removed, or enhanced (thus removing the need for the library sort), or D needs to make it possible to overload built-in properties.
 
 which I /think/ would work, because of type-deduction. (The compiler
 will match it with sort!(delegate)). If it doesn't, then you could
 file a bugzilla report that type deduction fails, and eventually it
 will be fixed. In short, the solution to your problem is to REMOVE the
 built-in property "sort" from arrays, so that libary versions can take
 over.

OK, that is all I really wanted in the first place. my issue was with the syntax. if it is implemented with templates but I get to use the above syntax than I'm a 100% satisfied customer. :) also, I didn't mention it above but this also needs to support anonymous delegates too: array.sort((C a, C b) {return a.val < b.val;}); I assume this works too. correct me if this is wrong.
May 13 2008
next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  >     alias std.algorithm.sort!("b<a") sort_reverse;
  >     array.sort_reverse;
  >
  > Is that close enough?

  I want this alias in the library.

It can't be in the library, because if it were in the library it would cause the template to instantiate. We don't want it to instantiate if it's not needed. (Currently, an alias causes instantiation, even if the alias is not subsequently used. I don't like that behaviour, but that's how it is right now).

I don't like it either.
 
 
 
  D needs to make it possible
  to overload built-in properties.

You can't overload something that isn't a class. Overloading requires inheritance.

either D should provide special treatment for this or it shoud use one of the other options like removing the built-in sort. Besides we are discussing changes to D, aren't we? although I'm still not sure that parameterizing sort with an alias for the delegate makes less instances than parameterizing sort with array element type. could you explain and compare the two options in this regard? (this of course doesn't take into account the string mixin instances) of course, this is secondary to the syntax issue for me.
May 13 2008
prev sibling next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 2008/5/13 Janice Caron <caron800 googlemail.com>:
  >  D needs to make it possible
  >  to overload built-in properties.

  You can't overload something that isn't a class. Overloading requires
  inheritance.

My apologies - I think we both meant "override", not "overload". :-) You can't overLOAD unless you are the author of the struct/class in question.

agreed :)
May 13 2008
prev sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Janice Caron wrote:
 2008/5/13 Frits van Bommel <fvbommel remwovexcapss.nl>:
  Overloading works fine without a class:
  You're probably thinking of overriding, which requires a virtual method in
 a base class.

If you'd read a few posts on you would have realised I corrected that mistake within /seconds/ of the original post. :-)

And if you'd replied to your own post with that correction instead of re-replying to the parent post, I *would* have read that first :P. Anyway, I'd already canceled my post when you posted yours...
May 13 2008
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
Janice Caron wrote:
 2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  I want the following API:
  (I don't care now for the internal implementation and the wrapper
  functions/aliases should be provided by the standard library)

  array.sort; //same as array.sort(ASC);

That one already works.
  array.sort(DESC);

alias std.algorithm.sort!("b<a") sort_reverse; array.sort_reverse; Is that close enough?
  array.sort(someStaticComperator);
  array.sort(aDelegateComperator);
  array.someOtherfunction(params);

That will never be possible either with or without templates. Guess why? Well, to save you guessing, I'll tell you. It's because "sort" is a built-in property of D's dynamic arrays. (Like length and ptr are built in). In general, we can define a function foo upon arrays which can be called as either foo(array,otherParams); array.foo(otherParams); But unfortunately, you can't do that if the function happens to be named "sort". (Likewise "length", "ptr", "init", and so on). As I said, this is /not/ a limitation of templates.

Wow, then tango.core.Array.sort must work by magic! I had no idea Tango had such powers: C:\code\src\d\test>cat test.d import tango.core.Array; void main() { int[] buf = ([1,2,3]).dup; buf.sort( (int a, int b){ return a < b; } ); } C:\code\src\d\test>dmd test C:\code\src\d\test> Sean
May 13 2008
prev sibling next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Yigal Chripun escribió:
 my issues with the use of this syntax:
 a) what if I had a typo? what if i typed n instead of b (they are next
 to each other on my keyboard)? your solution or any other string
 solution cannot check this, since it is syntactically valid D code
 inside the string.
 b) if I want to compare q{a.member > b.member} there is no check that
 member exists, or any other type checking. the error would occur in the
 template code itself not in the string in both of the above examples.
 c) what if I forgot to type the "q"?
 d) most IDEs mark strings with a different color or something like that
 so I treat them automatically as data and not code. ( this is a minor
 issue, since IDEs could be made to work differently. but, my
 sub-conscience response to strings as "not code" is harder to fix)

I mostly agree with you. The problem with defining these things is strings is that... they are strings! They are not bind to symbols in the language. So in that sense you can't expect any help from an IDE: no autocompletion, no refactor, no go to definition, no nothing. If you take a look at two top languages of the moment (C# and Java) you can see the designers take careful steps to make the language good, eficcient, and IDE-enabled (making an IDE for those languages is relatively easy). Even for extension methods C# makes a lot of restrictions: they must be defined in a static class, and the first parameter must have a "this". In that way, an IDE must only search in static classes, and in those, just the methods that have a "this". In D, if there are no restrictions, you could potentially need to make a big search in order to see where an extension method can or cannot be proposed.
May 12 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Ary Borenszweig wrote:
 I mostly agree with you. The problem with defining these things is
 strings is that... they are strings! They are not bind to symbols in the
 language. So in that sense you can't expect any help from an IDE: no
 autocompletion, no refactor, no go to definition, no nothing.
 
 If you take a look at two top languages of the moment (C# and Java) you
 can see the designers take careful steps to make the language good,
 eficcient, and IDE-enabled (making an IDE for those languages is
 relatively easy).
 
 Even for extension methods C# makes a lot of restrictions: they must be
 defined in a static class, and the first parameter must have a "this".
 In that way, an IDE must only search in static classes, and in those,
 just the methods that have a "this". In D, if there are no restrictions,
 you could potentially need to make a big search in order to see where an
 extension method can or cannot be proposed.

Good Points! I thought about the IDE issue, but I wasn't sure how the IDE handles this.
May 12 2008
next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  Good Points! I thought about the IDE issue, but I wasn't sure how the
  IDE handles this.

I don't have an IDE, but in my text editor, "a < b" is colored like a string, but q{ a < b } is colored like D code throughout. So I can choose my quoting method depending on how I want my code colored. This works, not because my IDE is smart, but precisely because it isn't! It doesn't realise that q{...} is a string, so it gets colored like code.

auto-complete and similar features which cannot be provided for strings.
May 13 2008
prev sibling parent Ary Borenszweig <ary esperanto.org.ar> writes:
Janice Caron escribió:
 2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  Good Points! I thought about the IDE issue, but I wasn't sure how the
  IDE handles this.

I don't have an IDE, but in my text editor, "a < b" is colored like a string, but q{ a < b } is colored like D code throughout. So I can choose my quoting method depending on how I want my code colored. This works, not because my IDE is smart, but precisely because it isn't! It doesn't realise that q{...} is a string, so it gets colored like code.

That's just because normally most editors are configured so that "" and '' are colored as strings, so Walter chose an uncommon combination that won't get colored. But Descent colors q{...} as a string because it understands D and because... because it *is* a string! :-) Also, same comment as Yigal: the coloring is the least important feature in my previous post.
May 13 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Frits van Bommel <fvbommel remwovexcapss.nl>:
  Overloading works fine without a class:
  You're probably thinking of overriding, which requires a virtual method in
 a base class.

If you'd read a few posts on you would have realised I corrected that mistake within /seconds/ of the original post. :-)
May 13 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Dee Girl <deegirl noreply.com>:
  int array[] = [1, 2, 3];
  int x = 5;
  sort!((int a, int b) { return a + x < b + x; })(array);

  Two questions not one ^_^

  1. Is the code inside sort!() as powerful as delegate?

The code inside sort!() /is/ a delegate. It's an anonymous delegate, but its implementation is exactly the same as a delegate with a name. It's equivalent to int foo (int a, int b) { return a + x < b + x; }; sort!(foo)(array);
May 13 2008
prev sibling parent "Koroskin Denis" <2korden gmail.com> writes:
On Tue, 13 May 2008 11:59:16 +0400, Yigal Chripun <yigal100 gmail.com>  
wrote:
 foo _is_ a delegate.
 there is no difference: As I explained in a different post, a delegate
 is simply two pointers where the context pointer either points to the
 surrounding function for nested functions or to the this pointer for
 methods.

No, it is not. It's just a function, that takes additional pointer-to-environment passed as a first (hidden) parameter (just like `this` inside member functions). Note that you can't call it outside of the scope (since there would be no context pointer) unless you make delegate out of it (taking its address). That way, scope pointer stored in a delegate as well. That's all.
May 13 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  I want the following API:
  (I don't care now for the internal implementation and the wrapper
  functions/aliases should be provided by the standard library)

  array.sort; //same as array.sort(ASC);

That one already works.
  array.sort(DESC);

alias std.algorithm.sort!("b<a") sort_reverse; array.sort_reverse; Is that close enough?
  array.sort(someStaticComperator);
  array.sort(aDelegateComperator);
  array.someOtherfunction(params);

That will never be possible either with or without templates. Guess why? Well, to save you guessing, I'll tell you. It's because "sort" is a built-in property of D's dynamic arrays. (Like length and ptr are built in). In general, we can define a function foo upon arrays which can be called as either foo(array,otherParams); array.foo(otherParams); But unfortunately, you can't do that if the function happens to be named "sort". (Likewise "length", "ptr", "init", and so on). As I said, this is /not/ a limitation of templates. If Walter were to remove the built-in sort property of arrays, then all of a sudden you would have: array.sort(someStaticComperator); array.sort(aDelegateComperator); which I /think/ would work, because of type-deduction. (The compiler will match it with sort!(delegate)). If it doesn't, then you could file a bugzilla report that type deduction fails, and eventually it will be fixed. In short, the solution to your problem is to REMOVE the built-in property "sort" from arrays, so that libary versions can take over.
May 13 2008
prev sibling next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  BUT, the main difference
  is that for C++ this is probably the only reasonable solution as C++
  doesn't have delegates.

C++ has functors (classes which overload operator ()), and they're a bit like delgates. What C++ doesn't have is alias template parameters, nor string template parameters. sort!("a.member > b.member")(array) simply couldn't be done in C++. This is /not/ a C++ solution. It's a D solution, and a fantastically brilliant one. Perhaps what you mean when you say "C++ solution", is, "it uses templates"?

it mis-uses templates, as I said in my previous post. it's the same C++ mind set. the only difference is that it utilizes more D++ features to accomplish the same thing. Personally, I prefer a smalltalk like collections framework over the c++ generic templates solution. it makes so much more sense to me to have a orderedCollection interface which defines a sort method, than a c++ generic sort template. It makes more sense to me to write: array.sort(); or array.sort(aDelegate); instead of using: sort(array); or sort(array, aDelegate); or even worse: sort!(aDelegate)(array); C++ avoids using real OOP encapsulation since it perceives it as adding unnecessary overhead. this is plain wrong since the overhead is orthogonal to the design principles. This is why C++ is adding the notion of concepts (which facilitate compile time OOP) to the next standard. --Yigal
May 11 2008
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
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
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
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
parent reply Yigal Chripun <yigal100 gmail.com> writes:
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.

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.

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
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 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?

Umm. Yes. auto col = new LinkedList!(T); sort(col); becomes auto col = new Vector!(T); sort(col);

and what about my function? void WalkListAndDoSomething(List!(T) list); would become what? void WalkListAndDoSomething(auto list); I wonder if the compiler will accept that.. There is no such thing as List!(T) in the STL design. either you use a vector or you use a linked list. there's nothing in between.
 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.

But ... what if it isn't? What if I import some.other.Collection which doesn't support your interface? All of a sudden, my ability to sort vanishes. You can argue that that is entirely the fault of some.other.Collection, for not implementing the interface, but come on! If I want to create a collection, frankly, I just want to implement the ability to put things in and get things out and store things efficiently. I don't want to have to muck about re-inventing the wheel by implementing stable sort, over and and over again for each new collection.

But you are re-inventing the wheel! the entire point is to have a collection framework in the standard library. No one needs to write a list in Java or C#. Those frameworks are built to be extend-able if you in that much of a need of a different specialized collection. but than again, if you are writing a new specialized version instead of using the standard version that comes with the standard library than you would want to implement your highly optimized sort function as well.
 
 
 
  another benefit is that each derived class can provide a specialized and
  optimized version for its own internal structure. Iterators add overhead
  here

No they don't. All they do is expose the ability to access the collection, which you need to have anyway.

iterator object. for most simple traversals, that's an unnecessary overhead when I could just use the collection.each method with a delegate like I can do in Ruby/Smalltalk/other high level languages than don't force me to manually advance an iterator to the next object in the collection. (yes, D does that for me with foreach, yet it is another level of indirectness) Iterators are useful when I really want to control myself the order of traversal or stuff like that. if I just want to sum the values of all the objects in the collection, than an iterator is an overhead. guess what kind of action is more common.
 
 
 for no reason. oh there is one reason, generality... right?

There are several reasons. Buglessness would be a good one, in my eyes. I know that if I do sort(whatever) using std.algorithm, then there will be no bugs, even if "whatever" is a third-party collection. (That's assuming of course that std.algorithm.sort has been thoroughly debugged, but with a standard library one would certainly expect that). Whereas, if some third-party-collection has reinvented the wheel and implemented their own sort ... again ... well, that's just one more place for bugs to creep in. And of course, it's not just sort(). std.algorithm also provides functions like reduce(), filter(), find(), count(), and a gazillion more, all highly customizable. You want all of those in your container interface, so that container implementors have to implement all of them every time? Frankly, it would be irresponsibility of a standard library /not/ to provide reusable implementations.

nothing has to be either/or. D provides many mechanisms such as mixins for example that could be used. and the standard library can provide general functions too. if I use the container in the standard library, I can use the built-in collection.sort(); and if i use a third-party collection, i could also use thirdPartyCollection.sort(); [that will use the template solution and the proposed D2 extension for open classes, I've mentioned a few times] or if you prefer, sort(thirdPartyCollection); besides, all those templates could double-up as code the collection writers can mix-in into their collection classes to implement the interfaces. this way, a third party vendor can decide to be compatible to the D standard library and implement all those interfaces with the provided mixins, and in case his collection isn't compatible the user could use the same templates himself. that's a win-win situation.
 
 
 
  fortunately, D does provide all the necessary language
  support, so we are not limited by iterators any more.

Your use of language is prejudicial here. Note that I can substitute anything I don't like into that sentence. For example: "Fortunately, D does provide all the necessary language support, so we are not limited by foreach any more", or "Fortunately, D does provide all the necessary language support, so we are not limited by polymorphism any more", or... We are not limited, period. We have choices. Why not just say that?

aren't available or if they are they are much harder to accomplish.
May 11 2008
parent reply "Koroskin Denis" <2korden gmail.com> writes:
On Sun, 11 May 2008 19:47:14 +0400, Yigal Chripun <yigal100 gmail.com>  =

wrote:

 Janice Caron wrote:
 On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 I want to be able to do this:
  List!(T) col =3D 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?



Well, yes, is possible. Even with STL. A typical trick I often do is this: typedef std::vector<Item*> Items; Items items; items.pushBack(new Item()); Item* item =3D items.front(); Items::iterator it =3D std::find(items.begin(), items.end(), someItem); if (it !=3D items.end()) { items.erase(it); } std::sort(items.begin(), items.end()); // etc And now if I decide to exchange vector with list or deque or anything el= se, all I need to do is to change a single line: typedef std::list<Item*> Items; I don't say that it is the best way to stick with, but it usually works.=
 ight...
 and what about my function?
 void WalkListAndDoSomething(List!(T) list);

Use templates. Or use polymorhism, if you expect containers to implement= = some generic interface.
May 11 2008
next sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Koroskin Denis wrote:
 On Sun, 11 May 2008 19:47:14 +0400, Yigal Chripun <yigal100 gmail.com>
 wrote:
 
 Janice Caron wrote:
 On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 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?



Well, yes, is possible. Even with STL. A typical trick I often do is this: typedef std::vector<Item*> Items; Items items; items.pushBack(new Item()); Item* item = items.front(); Items::iterator it = std::find(items.begin(), items.end(), someItem); if (it != items.end()) { items.erase(it); } std::sort(items.begin(), items.end()); // etc And now if I decide to exchange vector with list or deque or anything else, all I need to do is to change a single line: typedef std::list<Item*> Items; I don't say that it is the best way to stick with, but it usually works.
 ight...
 and what about my function?
 void WalkListAndDoSomething(List!(T) list);

Use templates. Or use polymorhism, if you expect containers to implement some generic interface.

all I can say is yuk. i can use tricks like that, or i can just use D's interface. I personally prefer the later. thanks for the info, though.
May 11 2008
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
Koroskin Denis wrote:
 Use templates. Or use polymorhism, if you expect containers to implement 
 some generic interface.

Problem with using interfaces or base classes is, you can't do the following with interfaces, and you can't do it efficiently with base classes: interface Collection(T) { void addRange(U : T)(Collection!(U) collection); } While having an abstract class Collection that defines that template would make the end users a bit happier, it'd basically boil down to: void addRange(U : T)(Collection!(U) collection) { this.reserve(collection.length); foreach (u; collection) this.add(u); }
May 11 2008
prev sibling next sibling parent terranium <spam here.lot> writes:
Janice Caron Wrote:

     sort!("a.member > b.member")(array)
 
 simply couldn't be done in C++. This is /not/ a C++ solution. It's a D
 solution, and a fantastically brilliant one.

does it have any use? Maybe implementing LINQ will be much more powerful and readable?
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 Umm. Yes.

> auto col = new LinkedList!(T); > sort(col); > > becomes > > auto col = new Vector!(T); > sort(col); > right... and what about my function? void WalkListAndDoSomething(List!(T) list); would become what?

List!(T) list; map!(doSomething)(list); or List!(T) list; reduce!(doSomething)(x,list); or List!(T) list; inPlace!(doSomething)(list); depending on which was more appropriate for doSomething.
  void WalkListAndDoSomething(auto list);
  I wonder if the compiler will accept that..

I'm baffled as to why you even said that. You know it won't. It's silly.
  There is no such thing as List!(T) in the STL design. either you use a
  vector or you use a linked list. there's nothing in between.

I thought we were talking D here? I took your use of "List!(T)" there to mean any arbitrary collection.
 But you are re-inventing the wheel!

No I'm not.
 the entire point is

Again, you're telling me there's this "the" point, which is different from "a" point, and somehow more important than any point I might make.
 to have a
 collection framework in the standard library.

Again, you're criticising today's D, and failing to acknowledge that tomorrow's D may (and almost certainly will) have a collection framework in the standard library.
 the overhead [of iterators] is a separate object, and function calls for the
 iterator object.

By "object" you presumably mean struct - often times small enough to pass around in a register. There is essentially no difference between a struct containing a pointer, and a pointer. By "function" you presumably mean static function, which takes zero space in the struct, and will almost certainly be inlined when compiled. Overhead gone.
 for most simple traversals, that's an unnecessary
 overhead when I could just use the collection.each method with a
 delegate

Now that /does/ have a function call overhead.
  if I just want
  to sum the values of all the objects in the collection, than an iterator
  is an overhead.

Or you could just do int sum = reduce!("a+b")(0,collection); :-)
  besides, all those templates could double-up as code the collection
  writers can mix-in into their collection classes to implement the
  interfaces.

Then aren't we lucky they exist! :-)
  that's a win-win situation.

Yes.
May 11 2008
prev sibling next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Your example does use delegates.
 the difference is this:
 say you have two delegates you pass to sort, for example you sort one
 array in ascending order, and another in descending order. the template
 solution will generate two almost identical sort functions with the only
 difference that one sort instance calls internally the first delegate,
 and the second instance calls the second. you get 4 functions. without
 the template, you get one sort function and two delegates, so only 3
 functions.

This should be good for the standard library. It is the most flexible approach. You can always do what you want in one line: void mysort(int array[], bool delegate comp(int, int)) { sort!(comp)(array); } Now you can call mysort many times and there is only one version of std.sort (if I understand it correctly). But think what if your mysort is the only implementation given in the standard library. Maybe sometimes it is too slow for your program. What do you do? void fastsort!(alias comp)(int[] array) { bool dg(int a, int b) { return comp(a, b); } mysort(array, dg); } This will not be faster at all ^_^. As a friend said: You can build a dynamic library from a static one but not inverse. Things only stack one direction. I think it is good the way sort is now in the standard library, it offers the maximum possible and opens all options. It is very impressive for someone coming from many other language.
 Now, when you use a string with the template, the sort will contain the
 code itself inline to the sort instance so it "saves" you a function
 call. IMO, the string solution is not clean ( C MACROs? )and while for
 simple cases it could save you a function call it's premature
 optimization IMO.

To me it looks very convinent!
 Also, if I use delegates, I can't see how a template
 can save that function call. so it just bloats my executable with
 unneeded copies of sort.

You can use mysort above. It is my first contribution to D. ^_^
 I hate when library code forces me to this. I think the programmer knows
 his code better than the library writer and should be able to choose
 himself whether he needs to use a compile time solution to save function
 calls or a run-time solution to prevent unnecessary executable bloat.
 this optimization should be made by the programmer and _not_ the library
 writer. This solution does not give me that freedom.

It looks like you understand the exact oposite. std.sort gives you all freedom, anything else would give less. I have studied many languages but none offers the same flexibility. Functional languages force you to copy. Java, Smalltalk and C# force virtual calls on comparisons. C++ force you to go outside your function and write the comparison even simple as a.member < b.member! D is the best and std.algorithm very elegant.
 this solution is a classic STL piece of code, BUT, the main difference
 is that for C++ this is probably the only reasonable solution as C++
 doesn't have delegates. this is why when coding for D I urge people to
 rethink their solution instead of just doing what is best in C++ (or any
 other preferred language of the library writer). This is a prime example
 why the C++ solution is wrong for D.

The other rant you wrote and the interesting discussion with Janice are good to read. But there is little objective points you bring. I see you do not like anything like C++ and then you go back and try to make arguments. One objective point is you like a container hierarchy. I have worked with them and they can be useful. But like with mysort the same argument works. You can build a hierarchy of containers on top of fast containers. But not the inverse. Dee Girl
May 11 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:
 Yigal Chripun Wrote:
 
 
 This should be good for the standard library. It is the most flexible
 approach. You can always do what you want in one line:
 
 void mysort(int array[], bool delegate comp(int, int)) { 
 sort!(comp)(array); }
 
 Now you can call mysort many times and there is only one version of
 std.sort (if I understand it correctly).

not exactly. there's always a balance between memory and time, if you improve the first second worsens. for each different comp function there will be a different sort instance created by the compiler. a simple example: struct num(N) { int number = N; } num!(1) is different from num!(2); so with this method you'll get a bigger executable that contains the sort code twice. with my way on the other hand you'll always have only one sort function. the cost in my case is that sort will always call the comp function to compare while the template can avoid that function call. simply time vs. memory. memory is cheap nowadays and the linker could be made better to try to minimize the memory footprint. on the other hand cpu chips today come with multi-cores and the compiler could optimize many calls to small functions. so in the obj file, the compiler can inline the function body instead of calling it. the main question would be: what are you optimizing for? if you develop on a cellphone memory could be more important then time, but on a missile a nano second could mean the missile missed its target. so it all depends on what you want to achieve.
 
 But think what if your mysort is the only implementation given in the
 standard library. Maybe sometimes it is too slow for your program.
 What do you do?
 
 void fastsort!(alias comp)(int[] array) { bool dg(int a, int b) {
 return comp(a, b); } mysort(array, dg); }
 
 This will not be faster at all ^_^. As a friend said: You can build a
 dynamic library from a static one but not inverse. Things only stack
 one direction. I think it is good the way sort is now in the standard
 library, it offers the maximum possible and opens all options. It is
 very impressive for someone coming from many other language.
 
 Now, when you use a string with the template, the sort will contain
 the code itself inline to the sort instance so it "saves" you a
 function call. IMO, the string solution is not clean ( C MACROs?
 )and while for simple cases it could save you a function call it's
 premature optimization IMO.

To me it looks very convinent!
 Also, if I use delegates, I can't see how a template can save that
 function call. so it just bloats my executable with unneeded copies
 of sort.

You can use mysort above. It is my first contribution to D. ^_^
 I hate when library code forces me to this. I think the programmer
 knows his code better than the library writer and should be able to
 choose himself whether he needs to use a compile time solution to
 save function calls or a run-time solution to prevent unnecessary
 executable bloat. this optimization should be made by the
 programmer and _not_ the library writer. This solution does not
 give me that freedom.

It looks like you understand the exact oposite. std.sort gives you all freedom, anything else would give less. I have studied many languages but none offers the same flexibility. Functional languages force you to copy. Java, Smalltalk and C# force virtual calls on comparisons. C++ force you to go outside your function and write the comparison even simple as a.member < b.member! D is the best and std.algorithm very elegant.
 this solution is a classic STL piece of code, BUT, the main
 difference is that for C++ this is probably the only reasonable
 solution as C++ doesn't have delegates. this is why when coding for
 D I urge people to rethink their solution instead of just doing
 what is best in C++ (or any other preferred language of the library
 writer). This is a prime example why the C++ solution is wrong for
 D.

The other rant you wrote and the interesting discussion with Janice are good to read. But there is little objective points you bring. I see you do not like anything like C++ and then you go back and try to make arguments. One objective point is you like a container hierarchy. I have worked with them and they can be useful. But like with mysort the same argument works. You can build a hierarchy of containers on top of fast containers. But not the inverse. Dee Girl

My rant emphasizes specific things, since that's the point of a rant. I could talk about all the things I like in C++ and Janice would agree and we won't have anything interesting to talk about. I do use c++ and it is a powerful language. My point is not that I'm against templates, or that I hate C++. What i'm trying to say is that using any one tool for everything is wrong. I'm not arguing that delegates should replace all template code in D! I'm arguing that both solutions need to be evaluated and the better one chosen, also note that these two options overlap sometimes and (when it's suitable) both solutions should be provided so the user could choose his preferred trade-off (time vs. memory). --Yigal
May 11 2008
next sibling parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 Dee Girl wrote:
 Yigal Chripun Wrote:
 
 
 This should be good for the standard library. It is the most flexible
 approach. You can always do what you want in one line:
 
 void mysort(int array[], bool delegate comp(int, int)) { 
 sort!(comp)(array); }
 
 Now you can call mysort many times and there is only one version of
 std.sort (if I understand it correctly).

not exactly. there's always a balance between memory and time, if you improve the first second worsens. for each different comp function there will be a different sort instance created by the compiler.

I do not think so. In fact I am sure you are entirely wrong this time. mysort has a signature that is not parameterized. The language definitions (I just read again) implies the model of compiling is that not-parameterized functions have only one implementation. If mysort has only one implementation, it is not possible for mysort to use different instantiations of sort. It is called the pigeonhole principle. It uses only one instantiations for all calls. Unless D generates code at runtime. ^_^
 a simple example:
 struct num(N) { int number = N; }
 num!(1) is different from num!(2);
 so with this method you'll get a bigger executable that contains the
 sort code twice.

I understand that very well (better than English...) I did just implemented a compiler for a mini-pl0 language with generics in class. These problems of code duplication are very clear when I wrote the compiler my self! ^_^ But maybe you did not understand how mysort works without duplicating code.
 with my way on the other hand you'll always have only one sort function.
 the cost in my case is that sort will always call the comp function to
 compare while the template can avoid that function call. simply time vs.
 memory. memory is cheap nowadays and the linker could be made better to
 try to minimize the memory footprint. on the other hand cpu chips today
 come with multi-cores and the compiler could optimize many calls to
 small functions. so in the obj file, the compiler can inline the
 function body instead of calling it.
 the main question would be: what are you optimizing for?
 if you develop on a cellphone memory could be more important then time,
 but on a missile a nano second could mean the missile missed its target.
 so it all depends on what you want to achieve.

If you believe this then you should be happy with std.sort. Which gives the option to optimize for what you want. All other sort implementation I read only go one way and it's hard to make them go an other way.
 But think what if your mysort is the only implementation given in the
 standard library. Maybe sometimes it is too slow for your program.
 What do you do?
 
 void fastsort!(alias comp)(int[] array) { bool dg(int a, int b) {
 return comp(a, b); } mysort(array, dg); }
 
 This will not be faster at all ^_^. As a friend said: You can build a
 dynamic library from a static one but not inverse. Things only stack
 one direction. I think it is good the way sort is now in the standard
 library, it offers the maximum possible and opens all options. It is
 very impressive for someone coming from many other language.
 
 Now, when you use a string with the template, the sort will contain
 the code itself inline to the sort instance so it "saves" you a
 function call. IMO, the string solution is not clean ( C MACROs?
 )and while for simple cases it could save you a function call it's
 premature optimization IMO.

To me it looks very convinent!
 Also, if I use delegates, I can't see how a template can save that
 function call. so it just bloats my executable with unneeded copies
 of sort.

You can use mysort above. It is my first contribution to D. ^_^
 I hate when library code forces me to this. I think the programmer
 knows his code better than the library writer and should be able to
 choose himself whether he needs to use a compile time solution to
 save function calls or a run-time solution to prevent unnecessary
 executable bloat. this optimization should be made by the
 programmer and _not_ the library writer. This solution does not
 give me that freedom.

It looks like you understand the exact oposite. std.sort gives you all freedom, anything else would give less. I have studied many languages but none offers the same flexibility. Functional languages force you to copy. Java, Smalltalk and C# force virtual calls on comparisons. C++ force you to go outside your function and write the comparison even simple as a.member < b.member! D is the best and std.algorithm very elegant.
 this solution is a classic STL piece of code, BUT, the main
 difference is that for C++ this is probably the only reasonable
 solution as C++ doesn't have delegates. this is why when coding for
 D I urge people to rethink their solution instead of just doing
 what is best in C++ (or any other preferred language of the library
 writer). This is a prime example why the C++ solution is wrong for
 D.

The other rant you wrote and the interesting discussion with Janice are good to read. But there is little objective points you bring. I see you do not like anything like C++ and then you go back and try to make arguments. One objective point is you like a container hierarchy. I have worked with them and they can be useful. But like with mysort the same argument works. You can build a hierarchy of containers on top of fast containers. But not the inverse. Dee Girl

My rant emphasizes specific things, since that's the point of a rant. I could talk about all the things I like in C++ and Janice would agree and we won't have anything interesting to talk about. I do use c++ and it is a powerful language. My point is not that I'm against templates, or that I hate C++. What i'm trying to say is that using any one tool for everything is wrong.

But you want to use one tool for all job. You want indirect calls and they force your design only one way.
 I'm not arguing that delegates should replace all
 template code in D! I'm arguing that both solutions need to be evaluated
 and the better one chosen, also note that these two options overlap
 sometimes and (when it's suitable) both solutions should be provided so
 the user could choose his preferred trade-off (time vs. memory).

Maybe once it is clear the inclusion relationship: that with the template you can have dynamic. Then you will be happy. Dee Girl
May 11 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
your reply is correct but it is misleading.
a different example:
void Fn() {
  int i;
}

every time the function is called a new int i is generated on the stack
and destroyed when the function is exited. so for a specific call to Fn
there can be only one variable i, in the course of the program that same
variable is created and destroyed many times ( for each call to the
function).
you are correct in saying that there is only one mysort function but
every time you call it with a different comp delegate it needs to have a
different sort instance. the difference is that the int i is generated
on the stack but all those different sort instances are not. those are
code which goes into the executable.
so while in any specific time mysort uses only one sort instance, over
the course of the entire program the sort template can get instantiated
more than once.
does it make more sense to you, now?

--Yigal
May 11 2008
next sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 your reply is correct but it is misleading.

Whose reply? What is this a reply to? You might want to continue replying point-by-point. It makes things a lot easier to follow.
  you are correct in saying that there is only one mysort function

Who said that, and in what context?

  but
  every time you call it with a different comp delegate it needs to have a
  different sort instance.

OK, I see what you're saying. If I do sort!(English)(text); sort!(Czech)(text); then I do indeed get two instantiations (which presumably will sort text alphabetically by English and Czech rules respectively). HOWEVER. If I write void mysort(T)(T[] array, int delegate(T,T) compare) { sort!(compare)(array); } then I have implemented your desired solution, trivially. And now I can call mysort(array,English); mysort(array,Check); and still there's only one instantiation of sort. It's exactly what Dee's been saying all along. You can make dynamic from static, but you can't make static from dynamic.

I don't follow. How can there be only one sort instance? the mysort function uses both sorts so the code for both needs to be in the executable...the first call instantiates the first sort for English, and the second instantiates the second sort for Czech.
May 11 2008
next sibling parent Dee Girl <deegirl noreply.com> writes:
Janice Caron Wrote:

 Why is my last post not clear? You get two instantiations with
 
     sort!(English)(text);
     sort!(Czech)(text);
 
 because I statically passed in two separate delegates, but you only
 get one instantiation with
 
     sort!(compare)(array)
 
 because I statically passed in only one delegate. That you passed two
 into mysort is irrelevant, because that's a runtime thing.

Thank you. This is a clear explaining. It took me long time to write my massage with disassembling a program! I wish I saw your post first. I would not have to write it ^_^ Thank you, Dee Girl
May 11 2008
prev sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 I don't follow. How can there be only one sort instance?

Because I only instantiated it once - specifically with the delegate named "compare".
  the mysort function uses both sorts

No, the mysort function is handed a delegate an input parameter.
 so the code for both needs to be in
 the executable...

The comparison functions need to be in the executable, but not two separate instantiations of sort. Why is my last post not clear? You get two instantiations with sort!(English)(text); sort!(Czech)(text); because I statically passed in two separate delegates, but you only get one instantiation with sort!(compare)(array) because I statically passed in only one delegate. That you passed two into mysort is irrelevant, because that's a runtime thing.

OK, I think i understand you now. there is only one sort instance that calls compare. compare changes at run-time. in that case, I was wrong. sorry for the confusion.
May 11 2008
prev sibling parent reply Dee Girl <deegirl noreply.com> writes:
Yigal Chripun Wrote:

 your reply is correct but it is misleading.
 a different example:
 void Fn() {
   int i;
 }
 
 every time the function is called a new int i is generated on the stack
 and destroyed when the function is exited. so for a specific call to Fn
 there can be only one variable i, in the course of the program that same
 variable is created and destroyed many times ( for each call to the
 function).
 you are correct in saying that there is only one mysort function but
 every time you call it with a different comp delegate it needs to have a
 different sort instance. the difference is that the int i is generated
 on the stack but all those different sort instances are not. those are
 code which goes into the executable.
 so while in any specific time mysort uses only one sort instance, over
 the course of the entire program the sort template can get instantiated
 more than once.
 does it make more sense to you, now?

I am sorry Yigal. What you say makes no sense. Your previous reply is wrong and this reply is even more wrong. It is simple logic. mysort has only one body. Inside that body is has a regular call to a function that implements the sorting. That call can only go to one function! It is very clear. D does not generate code at runtime. It is a silly exercise but we can do this. Please compile this program. === test.d #!/home/yasuko/bin/compile-launch -w import std.algorithm; import std.stdio; void mysort(int[] array, bool delegate(int, int) comp) { sort!(comp)(array); } void main() { int[] array = [ 1, 2, 3, 4 ]; bool comp1(int a, int b) { return a > b; } mysort(array, &comp1); //writeln(array); bool comp2(int a, int b) { return a < b; } mysort(array, &comp2); //sort!(comp2)(array); //writeln(array); } === writeln is taken out so it does not get confusing. Please run dmd -g test.d objdump -d test.o|less Search for word "section" in the disassembled file. There are 19. It seems each function has its section. 1. section .text is the all file 2. section .text._D4test6mysortFAiDFiiZbZv is the mysort function. I do not know the naming scheme of D but it is easy to guess which function is. 3. section .text._Dmain is the main funciton 4. section .text._D4test4mainFZv8compare1MFiiZb is compare1 5. section .text._D4test4mainFZv8compare2MFiiZb is compare2 6. section .text._D4test6mysortFAiDFiiZbZv108__T4sortS36_D4test6mysortFAiDFiiZbZv4compDFiiZbVE3std9algorithm12SwapStrategy0S233std9algorithm8ite SwapTAiZ4sortMFAiZv is the std.sort function 7. section .text._D3std8iterator12__T5beginTiZ5beginFAiZPi is begin funciton 8. section .text._D3std8iterator10__T3endTiZ3endFAiZPi is end function 9. section .text._D4test6mysortFAiDFiiZbZv112__T8sortImplS36_D4test6mysortFAiDFiiZbZv4compDFiiZbVE3std9algorithm12SwapStrategy0S233std9algorithm8iterSwa TAiZ8sortImplMFAiZv is the sortimpl function 10. section .text._D4test6mysortFAiDFiiZbZv112__T8sortImplS36_D4test6mysortFAiDFiiZbZv4compDFiiZbVE3std9algorithm12SwapStrategy0S233std9algorithm8iterSwapTAiZ8sort mplMFAiZv4predMFiZb is the pred function 11. section .text._D4test6mysortFAiDFiiZbZv55__T8getPivotS36_D4test6mysortFAiDFiiZbZv4compDFiiZbTP Z8getPivotMFPiPiZPi is the getPivot function 12. section .text._D3std9algorithm16__T8iterSwapTPiZ8iterSwapFPiPiZv is the iterSwap function 13. section .text._D3std9contracts17__T8pointsToTiTiZ8pointsToFKiKiZb is the pointsTo function 14. section .text._D3std9algorithm11__T4swapTiZ4swapFKiKiZv is the swap function 15. section .text._D3std8iterator12__T5rangeTiZ5rangeFPiPiZAi is the range function 16. section .text._D4test6mysortFAiDFiiZbZv112__T8sortImplS36_D4test6mysortFAiDFiiZbZv4compDFiiZbVE3std9algorithm12SwapStrategy0S233std9algorithm8iterSwapTAiZ8sortImplMFAiZv243__T9partitionS165_D4test6mysortFAiDFiiZbZv112__T8sortImplS36_D4test6mysortFAiDFiiZbZv4compDFiiZbVE3std9algorithm12SwapStrategy0S233std9algorithm8iterSwapTAiZ8sortImplMFAiZv4predMFiZbVE3std9algorithm12SwapStrategy0S233std9algorithm8iterSwapT iZ9partitionMFAiZPi is the partition function 17. section .text._D4test6mysortFAiDFiiZbZv55__T8isSortedS36_D4test6mysortFAiDFiiZbZv4compDFiiZ TAiZ8isSortedMFAiZb is the isSorted function 18. section .text._D4test6mysortFAiDFiiZbZv55__T8isSortedS36_D4test6mysortFAiDFiiZbZv4compDFiiZbTAiZ8isSor edMFAiZb4predMFiiZb is another isSorted function (I do not now why there are two) 19. section .text._D4test6mysortFAiDFiiZbZv55__T8isSortedS36_D4test6mysortFAiDFiiZbZv4compDFiiZbTAiZ8isSortedMFAiZb133__T12findAdjacentS108_D4test6mysortFAiDFiiZbZv55__T8isSortedS36_D4test6mysortFAiDFiiZbZv4compDFiiZbTAiZ8isSortedMFAiZb4predMFiiZbTAiZ1 findAdjacentMFAiZPi is the isAdjacent function. It is clear now that there is only one instantiation of std.sort. I hope now it is all clear. Thank you, Dee Girl
May 11 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Dee Girl wrote:
<snip>
thanks for the effort explaining me the issue. I've already understood
my mistake thanks to Janice' explanation, though.
May 11 2008
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  for each different comp function there will be a different sort instance
  created by the compiler.

But surely, every container must itself be templatised on the element? I mean, unless you go the Java way and /only/ have a container for Objects. So your plan would instantiate at least one sort function for every element type. Moreover, since your OrderedCollection interface would demand that every container must implement sort, then every different kind of collection would have it's own sort function. So under your scheme, the total number of sort functions would equal the total number of container types. In other word, if your program used List!(char) list1, list2; List!(int) list3; List!(C) list4, list5; Array!(double) array1; then there would be four sort functions. In what way is that fewer than would be produced by sort(list1); sort(list2); sort(list3); sort(list4); sort(list5); sort(array1); ? The way I see it, we both get four. However, if choose /not/ to sort list3, then I only get three instantiations, but you still get four.
  What i'm trying to say is that using any one tool for
  everything is wrong.

You should have just said that to start with, because it's a statement of the obvious and no one will disagree with you. :-)
  I'm not arguing that delegates should replace all
  template code in D! I'm arguing that both solutions need to be evaluated
  and the better one chosen

Why are you assuming that that didn't happen?
  also note that these two options overlap
  sometimes and (when it's suitable) both solutions should be provided so
  the user could choose his preferred trade-off (time vs. memory).

Except that, if you can trivially create one from the other, then providing one is effectively providing both anyway.

From what I know, an efficient STL implementation would use C style containers with void* under the hood to minimize executable bloat. in a perfect world, my idealized container collection would do something similar. the interfaces need to use templates so that I have a list!(T) and such with a proper hierarchy, however under the hood all those List!(T) with different T types will use the same code. also, My idealized collection framework would use mixins extensively to share common code. so, if all those tools are combined properly, the amount of sort instances should be minimal. the Java way is the extreme opposite of the c++ way, I want something in the middle, Eiffel has the best design IMO, couple that API with C efficiency and we got a winner :) the idea is of course take the best from each language and combine it to something even better. BTW, you really should take a look at Eiffel. for example, it has multiple-inheritance implemented in a completely different way than C++ avoiding most of c++ problems with it. they have many novel ideas there that D can and should learn from. My perfect collection framework would be based on the Eiffel design, avoiding problems in the Java model and problems in the C++ model. --yigal
May 11 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 From what I know, an efficient STL implementation would use C style
  containers with void* under the hood to minimize executable bloat.

I /think/ I get what you're saying here, which is (correct me if I'm wrong, but) given class C {} class D {} then Vector!(C) c; Vector!(D) d; produces two instantiations of Vector! - one for C and one for D - and yet those instantiations will produce byte-for-byte identical machine code. However - this is a linker problem, not a template problem, and Walter has said that this will be fixed in the future. So in the future, whenever any two or more segments consist of identical bytes, the linker may keep one and throw the rest away. The moral of the story is: specify your intent, but leave the implementation to the compiler/linker. If you try to "outguess" the compiler, by instantiating only one single Vector!(void*), then in the long run, all you are doing is creating obfuscated code for short term gain.

I don't know about any plans to change the dm linker. it is currently written directly in assembly and highly optimized. any change would require a lot of work and probably a rewrite. also, if walter ever decides to work on the linker he should change the format of the object files. Personally i don't understand a great deal about that subject, but i got the impression in this NG from more knowledgeable people that such a change is needed. what i mean was doing something like this: for example for list - defining a regular LinkedListImpl class which holds void* as items, and then defining a class LinkedList(T) which uses internally the LinkedListImpl class. this way the templates and interface hierarchy is separate from the implementation. the templatized interfaces and classes in the hierarchy provide the high level API and the *Impl classes provide efficient implementations without bloating the executable code. you get a different instances of vector as per your example for each unique type T, BUT those instantiations are merely small shells that call common code in the matching vectorImpl class. that is not the same as using linkedList!(void*) since you do get a layer that provides the convenience and type safety. If the linker/compiler could automate this, that would be great. but it can be accomplished without that too.
May 11 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Janice Caron wrote:
 On 12/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 I don't know about any plans to change the dm linker.

You do now. :-) The following is an exact word-for-word quote, posted 23 Mar by Walter Bright: "Templatizing is the language solution. I know about the bloat problem, but that is really an implementation issue. I know the linker doesn't do it now, but identical blocks of generated code with different names should be merged."

something should be done in a specific way is not the same as saying one would work to make that something work in that specific way. Walter acknowledges some problems with the linker, he also said that fixing that is a huge amount of work for the reasons I've stated in a previous post. so that doesn't mean Walter is planning to fix those problems in the near future.
 
  what i mean was doing something like this:
  <snip>

And that's a perfectly reasonable strategy, but it only works for pointers to structs. (In D, it won't work for class references). Structs, by contrast, should probably be written into a collection by value, not by reference. In short, it's a C++-specific strategy.

maybe there is a way to adapt that way of thinking to D or maybe this would be optimized automatically by the liker in the future, as you say all i can say is that I hope the linker will be worked on...
May 11 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Yigal Chripun wrote:
 hmm... you're right... :(
 maybe there is a way to adapt that way of thinking to D or maybe this
 would be optimized automatically by the liker in the future, as you say
 all i can say is that I hope the linker will be worked on...

(light-bulb) since D has Object from which all the classes inherit (unlike C++) the same idea I previously suggested could be employed, only instead of void* use Object. again, since it is buried inside the implementation, you still get proper generic interfaces and classes. this sounds even cleaner than void*. what do you think?
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  > The type of a is the type of the array element. This is obvious.

 again, obvious to you, not that dude five years from now reading your
  code. documented is not the same as compiler checked, and we both know
  that documentation could get out of sync with the code.

I'm talking about the documentation for std.algorithm.sort. What are you talking about? It is perfectly reasonable to expect that anyone using std.algorithm.sort will have read the documentation on how to use it.
  also, why do I have to go and search the docs if the code is in front of me?

You don't. You learn how to use std.algorithm.sort, and then you use it, going back to the docs only occasionally. It's the same with anything. If I want to use a std::vector in C++, I'd better have read up on what std::vectors are and how to use them first. So yes, it's obvious what the "a" and "b" mean.
 The benefit is choice.

choice of what?

Of everything. Of having it do whatever you want, whatever that is.
 You've said so yourself, both templated and
 non-templated versions of the sort-with-delegate are identical.

I have said no such thing! (And I take great offense at being misquoted) I may have said that *one particular instantiation* of the template is identical to the delegate version. Fair enough. But I can also make different instantiations.
  you want those string templates (which i don't like) than fine, define
  them as templates and i won't use them. but the version with the
  delegate is needlessly a template.

There /is/ no "version with the delegate". But you can /make/ a version with a delegate out of the template. And those string parameters are brilliant! Today I wanted to strip out all underscores from a string. So I sat down to write the function - it's not really hard, after all - but then, with a flash of inspiration, I realised the function already exists. It is: filter!("a != '_'")(s); That is just /so/ amazing.
  ideally (not possible in current D) both forms should have the
  same syntax, so the end user (me) doesn't need to know nor care about
  what sort of implementation was chosen.

You haven't heard of aliases or wrapper functions then?
May 12 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  Good Points! I thought about the IDE issue, but I wasn't sure how the
  IDE handles this.

I don't have an IDE, but in my text editor, "a < b" is colored like a string, but q{ a < b } is colored like D code throughout. So I can choose my quoting method depending on how I want my code colored. This works, not because my IDE is smart, but precisely because it isn't! It doesn't realise that q{...} is a string, so it gets colored like code.
May 13 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  BUT, the main difference
  is that for C++ this is probably the only reasonable solution as C++
  doesn't have delegates.

C++ has functors (classes which overload operator ()), and they're a bit like delgates. What C++ doesn't have is alias template parameters, nor string template parameters. sort!("a.member > b.member")(array) simply couldn't be done in C++. This is /not/ a C++ solution. It's a D solution, and a fantastically brilliant one. Perhaps what you mean when you say "C++ solution", is, "it uses templates"?
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  Personally, I prefer <snip>

By your own words, that's just a personal preference.
  It makes more sense to me to write:
  array.sort(); or array.sort(aDelegate);
  instead of using:
  sort(array); or sort(array, aDelegate); or even worse:
  sort!(aDelegate)(array);

But now you're just saying "my syntax is better than your syntax", when both do the same thing. In addition, the D version is more powerful, as it allows you to say: sort!(f)(array) where f is a function (not a delegate). It also allows you to write sort!(dg, SwapStrategy.stable)(array) to get a stable sort instead of a non-stable (but faster) sort. This kind of flexibility offers way more power than delegates alone. Besides which, arrays cannot implement an orderedCollection interface, because arrays cannot implement interfaces, so you'd be limited only to sorting custom collection classes.
  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?
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
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.
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
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
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 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?

Umm. Yes. auto col = new LinkedList!(T); sort(col); becomes auto col = new Vector!(T); sort(col);
 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.

But ... what if it isn't? What if I import some.other.Collection which doesn't support your interface? All of a sudden, my ability to sort vanishes. You can argue that that is entirely the fault of some.other.Collection, for not implementing the interface, but come on! If I want to create a collection, frankly, I just want to implement the ability to put things in and get things out and store things efficiently. I don't want to have to muck about re-inventing the wheel by implementing stable sort, over and and over again for each new collection.
  another benefit is that each derived class can provide a specialized and
  optimized version for its own internal structure. Iterators add overhead
  here

No they don't. All they do is expose the ability to access the collection, which you need to have anyway.
 for no reason. oh there is one reason, generality... right?

There are several reasons. Buglessness would be a good one, in my eyes. I know that if I do sort(whatever) using std.algorithm, then there will be no bugs, even if "whatever" is a third-party collection. (That's assuming of course that std.algorithm.sort has been thoroughly debugged, but with a standard library one would certainly expect that). Whereas, if some third-party-collection has reinvented the wheel and implemented their own sort ... again ... well, that's just one more place for bugs to creep in. And of course, it's not just sort(). std.algorithm also provides functions like reduce(), filter(), find(), count(), and a gazillion more, all highly customizable. You want all of those in your container interface, so that container implementors have to implement all of them every time? Frankly, it would be irresponsibility of a standard library /not/ to provide reusable implementations.
  fortunately, D does provide all the necessary language
  support, so we are not limited by iterators any more.

Your use of language is prejudicial here. Note that I can substitute anything I don't like into that sentence. For example: "Fortunately, D does provide all the necessary language support, so we are not limited by foreach any more", or "Fortunately, D does provide all the necessary language support, so we are not limited by polymorphism any more", or... We are not limited, period. We have choices. Why not just say that?
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  for each different comp function there will be a different sort instance
  created by the compiler.

But surely, every container must itself be templatised on the element? I mean, unless you go the Java way and /only/ have a container for Objects. So your plan would instantiate at least one sort function for every element type. Moreover, since your OrderedCollection interface would demand that every container must implement sort, then every different kind of collection would have it's own sort function. So under your scheme, the total number of sort functions would equal the total number of container types. In other word, if your program used List!(char) list1, list2; List!(int) list3; List!(C) list4, list5; Array!(double) array1; then there would be four sort functions. In what way is that fewer than would be produced by sort(list1); sort(list2); sort(list3); sort(list4); sort(list5); sort(array1); ? The way I see it, we both get four. However, if choose /not/ to sort list3, then I only get three instantiations, but you still get four.
  What i'm trying to say is that using any one tool for
  everything is wrong.

You should have just said that to start with, because it's a statement of the obvious and no one will disagree with you. :-)
  I'm not arguing that delegates should replace all
  template code in D! I'm arguing that both solutions need to be evaluated
  and the better one chosen

Why are you assuming that that didn't happen?
  also note that these two options overlap
  sometimes and (when it's suitable) both solutions should be provided so
  the user could choose his preferred trade-off (time vs. memory).

Except that, if you can trivially create one from the other, then providing one is effectively providing both anyway.
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 your reply is correct but it is misleading.

Whose reply? What is this a reply to? You might want to continue replying point-by-point. It makes things a lot easier to follow.
  you are correct in saying that there is only one mysort function

Who said that, and in what context?
  but
  every time you call it with a different comp delegate it needs to have a
  different sort instance.

OK, I see what you're saying. If I do sort!(English)(text); sort!(Czech)(text); then I do indeed get two instantiations (which presumably will sort text alphabetically by English and Czech rules respectively). HOWEVER. If I write void mysort(T)(T[] array, int delegate(T,T) compare) { sort!(compare)(array); } then I have implemented your desired solution, trivially. And now I can call mysort(array,English); mysort(array,Check); and still there's only one instantiation of sort. It's exactly what Dee's been saying all along. You can make dynamic from static, but you can't make static from dynamic.
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 From what I know, an efficient STL implementation would use C style
  containers with void* under the hood to minimize executable bloat.

I /think/ I get what you're saying here, which is (correct me if I'm wrong, but) given class C {} class D {} then Vector!(C) c; Vector!(D) d; produces two instantiations of Vector! - one for C and one for D - and yet those instantiations will produce byte-for-byte identical machine code. However - this is a linker problem, not a template problem, and Walter has said that this will be fixed in the future. So in the future, whenever any two or more segments consist of identical bytes, the linker may keep one and throw the rest away. The moral of the story is: specify your intent, but leave the implementation to the compiler/linker. If you try to "outguess" the compiler, by instantiating only one single Vector!(void*), then in the long run, all you are doing is creating obfuscated code for short term gain.
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 I don't follow. How can there be only one sort instance?

Because I only instantiated it once - specifically with the delegate named "compare".
  the mysort function uses both sorts

No, the mysort function is handed a delegate an input parameter.
 so the code for both needs to be in
 the executable...

The comparison functions need to be in the executable, but not two separate instantiations of sort. Why is my last post not clear? You get two instantiations with sort!(English)(text); sort!(Czech)(text); because I statically passed in two separate delegates, but you only get one instantiation with sort!(compare)(array) because I statically passed in only one delegate. That you passed two into mysort is irrelevant, because that's a runtime thing.
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 I don't know about any plans to change the dm linker.

You do now. :-) The following is an exact word-for-word quote, posted 23 Mar by Walter Bright: "Templatizing is the language solution. I know about the bloat problem, but that is really an implementation issue. I know the linker doesn't do it now, but identical blocks of generated code with different names should be merged."
  what i mean was doing something like this:
  <snip>

And that's a perfectly reasonable strategy, but it only works for pointers to structs. (In D, it won't work for class references). Structs, by contrast, should probably be written into a collection by value, not by reference. In short, it's a C++-specific strategy.
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  I prefer:
  sort!((int a, int b) {return a.member > b.member;})(array);

ints don't have a member variable called .member, so presumably, you must have meant something like class C { int member; } C[] array; sort!(C a, C b){return a.member > b.member;})(array);
  this is ... more readable.

No it isn't. ;-)
  but in the future that other programmer will have no
  idea what those "a" and "b" are. especially since you didn't write the
  types of them.

Incorrect. "a" and "b" are the placeholders for the things being compared. This is well documented in the documentation for sort. The only way anyone could not know what the "a" and "b" were would be if they hadn't RTFM. The type of a is the type of the array element. This is obvious.
  there is no benefit in making it a template, IMO.

The benefit is choice.
May 12 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Sean Kelly <sean invisibleduck.org>:
  It depends what you mean by "powerful."  Passing a comparator as a template
parameter,
  as with sort!(), means that the choice of comparator must be made at
compile-time rather
  than run-time.  This may be problematic in some instances.

I think you meant to say, the choice of comparator /may/ be made at compile-time. Not must. sort!(compare)(array); // compile time sort(compare, array); // run time
May 13 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Yigal Chripun <yigal100 gmail.com>:
  >     alias std.algorithm.sort!("b<a") sort_reverse;
  >     array.sort_reverse;
  >
  > Is that close enough?

  I want this alias in the library.

It can't be in the library, because if it were in the library it would cause the template to instantiate. We don't want it to instantiate if it's not needed. (Currently, an alias causes instantiation, even if the alias is not subsequently used. I don't like that behaviour, but that's how it is right now).
  D needs to make it possible
  to overload built-in properties.

You can't overload something that isn't a class. Overloading requires inheritance.
May 13 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Janice Caron <caron800 googlemail.com>:
  >  D needs to make it possible
  >  to overload built-in properties.

  You can't overload something that isn't a class. Overloading requires
  inheritance.

My apologies - I think we both meant "override", not "overload". :-) You can't overLOAD unless you are the author of the struct/class in question.
May 13 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
2008/5/13 Koroskin Denis <2korden gmail.com>:
 foo _is_ a delegate.

No, it is not. It's just a function, that takes additional pointer-to-environment passed as a first (hidden) parameter

I'm sure Yigal intended to mean "&foo _is_ a delegate". I understood him, anyway.
May 13 2008
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Disclaimer: this post reflects my personal opinion only and should not
start yet another flame war about tango vs phobos. feel free to disagree
with everything I say.

based on what I know:
phobos uses the C IO. this is both bad for performance (yet another
layer) and the interface is ugly. Tango's IO much more powerful, more
efficient, and more flexible. the cost of it is that it's more complex
(compared to the  C-like functions phobos provides).
phobos is ill organized mixing the run-time library (which you as a user
should never touch) with the user code. Why is the GC internals in the
same library as the string manipulation functions?? also the namespace
in tango is much better organized into packages and modules (although,
sometimes it's too much, like the collections being nested in utils). I
prefer Tango's tango.io.Stdout vs std.stdio. I think Stdout is lame but
at least it's inside an IO package. what's the deal with using 8-letter
acronyms that are hard to remember anyway? we are not limited by DOS' 8
chars length for file names anymore.
Tango has much more functionality, is optimized for performance, has
less bugs and more maintainers than phobos. Walter is a genius compiler
writer but he can't spend all his time on phobos, And I really dislike
the C++ smell from all of the code Andrei added to phobos. He's a C++
Wizard, But that C++ mindset that templates solve everything is bad IMO.
sometimes, the language does need to provide proper syntax. D needs a
good tuple support and not a template Tuple!(...)  for example. another
example would be all the templates in std.algorithm. I'd prefer a reduce
function that takes a delegate rather than a reduce template.

Regarding C++ and STL: it is useful for C++, however it is designed in a
way to take into account all the deficiencies of C++ (which D doesn't
have). an error in your c++ code that uses STL will produce a screen
full of text which is unrelated to the problem, making it useless. STL
does not use polymorphism in any way so that if i used an iterator and
decided to reverse the iteration I need to make sure I change the
Iterator type, which is lame (had this bug, once). this complete lack of
polymorphism is plain wrong. simple example: I use a vector to go over
series of values and then decide to use a linked-list instead. I need to
go and change my code everywhere, since there are no common interfaces
like Java's List or collection for that matter. in java i can have:
List<C> list = new ArrayList<C> ();
changed to:
List<C> list = new LinkedList<C> ();
and that's it! no need to search and replace all my functions (since
they use the general List<C> anyway. Isn't that so much more productive?
 C++ needs to add language extensions (i.e. concepts) to make that work.
My point is that I do not want that design of the STL. also, C++ uses
iterators extensively, and this design can be replaced by more advanced
concepts like generators and HOF. I prefer smalltalk and ruby way of
collection.each { code } vs. C++ more primitive:
for (collection::iterator i = collection.begin(); i != collection.end()
; ++i) { code }
which one of those is better?
smalltalk has map:, select: inject:into: etc, built into the collections
classes, which is SO much better than using C++ iterators.
STL does provide similar constructs but they are so verbose that no one
ever uses them.
D needs to become a compiled efficient ruby-like language more than a
slightly more readable C++.

Those are my rumblings, If somebody got offended, than I'm truly sorry
for that. I just wish for a better D future.

--Yigal
May 10 2008
parent "Me Here" <p9e883002 sneakemail.com> writes:
Yigal Chripun wrote:

 D needs to become a compiled efficient ruby-like language more than a
 slightly more readable C++.

D'accord. --
May 10 2008
prev sibling parent Yigal Chripun <yigal100 gmail.com> writes:
Yigal Chripun wrote:
 IMHO, your reply makes perfect sense for C/C++ but not for D.
 specifically because D has other facilities to handle those cases.
 a dchar (or [w]char) _must_ always contain valid data. if you need to
 store other encodings you can use ubyte instead which does not limit you
 to a specific bit pattern (this is why D has it in the first place...)
 the above example of slices can be easily dealt with since (unlike in
 C/C++) D arrays know their length. this is similar to the fact that D
 checks bounds on arrays and throws on error (in debug mode) whereas
 C/C++ does not. IMO, the D implementation itself (both the compiler and
 the runtime) need to make sure chars are always valid. this should not
 be something optional added via a library.
 
 I agree with you notion of levels, I just think D provides for much
 better facilities for low-level coding compared to using unsafe C/C++
 conventions.
 
     int n;
     dchar c = cast(dchar)n;
     dchar d = cast!(dchar)n;
 
 in the above code, the second one should be used and it might throw. the
 first simply does not make any sense and should produce a compiler error
 because you cannot convert an int value to a dchar (unless it's a one
 digit int)
 
 <off topic rant>
 What worries me most about D is the fact that D becomes an extension to C++.
 The whole idea behind D was to create a new language without all the
 baggage and backward compatibility issues of C++.
 I don't want a slightly more readable version of C++ since I'll get that
 with C++0x.
 c++ programmers want D to have a D "stl" and a D boost. that's wrong!
 STL is badly designed and employs massive amounts of black magic that
 ordinary people do not understand. (I suffer at work while writing in
 C++). in what world does it make sense to mark an abstract method with
 "=0;" at the end, especially when the method is horizontally long and
 that gets to be off screen!
 D should be written with a D mindset which should be the best
 ingredients extracted from all those languages D got its influences
 from: java, C#, python, ruby, c/c++, etc. Tango is a good example of
 designing such a new D mindset, IMO. Phobos is not, since it's merely C
 code written with D syntax, with all those new shiny code Andrei added
 which is C++ code written with D syntax. I appreciate his great
 expertise in C++, but I already can use C++ libraries in C++ without
 learning a new language. D needs to be better. *much* better.
 </rant>
 
 --Yigal

I think I misread your example so I want to clarify: chars should contain only valid utf code points and not any other bit-pattern. since code-points need to be ordered in specific ways it make sense that the D standard library would provide methods that validate and/or fix utf strings. However, any other encoding must use ubyte arrays instead. What if: int num = ...; dchar ch = cast(dchar)num; dchar ch1 = cast!(dchar)num; ch would contain the bit pattern of the num-th code-point in the utf standard (throwing for numbers not in the utf encoding table) and the second cast would operate on the bit level (like an reinterpret_cast) and throw if the resulting dchar bit pattern is not valid. also, I'm leaning towards reporting all cast run time errors with exceptions (it's more consistent, since you cannot return null for primitives). no need for that special return null case. (if D had attributes, i would have suggested making a suppress-exceptions attribute for that purpose) --Yigal
May 10 2008
prev sibling parent reply terranium <spam here.lot> writes:
BCS Wrote:

 The major issue I have with this is that the construct that actually 
 does the downcast MUST also do the type check. Therefor it gets done 
 twice and this is  a bit of a performance issue.

current implementation of cast already does the typecheck, so there will be no performance issue.
May 10 2008
parent BCS <ao pathlink.com> writes:
Reply to terranium,

 BCS Wrote:
 
 The major issue I have with this is that the construct that actually
 does the downcast MUST also do the type check. Therefor it gets done
 twice and this is  a bit of a performance issue.
 

will be no performance issue.

but the proposal would requier *two* checks in the "use this as a B if it's a B" case. One for the code to "if" on and one in the actual conversion (to be safe).
May 10 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 09/05/2008, BCS <BCS pathlink.com> wrote:
  The major issue I have with this is that the construct that actually does
 the downcast MUST also do the type check. Therefor it gets done twice and
 this is  a bit of a performance issue.

Good point.
  I'd rather see a testClass!(T) version that acts exactly like the current
 cast and a isClass!(T) that acts like your class!(T).

OK, so how about just making class!(T) return null, and do away with is!(T)? That's what C++ does with dynamic_cast<T>, after all.
May 09 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 09/05/2008, Janice Caron <caron800 googlemail.com> wrote:
  >  I'd rather see a testClass!(T) version that acts exactly like the current
  > cast and a isClass!(T) that acts like your class!(T).

 OK, so how about just making class!(T) return null, and do away with
  is!(T)? That's what C++ does with dynamic_cast<T>, after all.

One goal of the proposal was to avoid introducing more keywords. Some of these conversions don't have to be keywords, of course. to! is already implemented in std.conv, and this proposal only suggests extending its reach. But others kinds of transformation are better done by the compiler, and I'd say that includes RTTI, so for that reason, I went for keyword reuse.
May 09 2008
prev sibling next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
I'm sorry if this post sounds argumentative, but I disagree strongly 
with pretty much everything in this post. So no hard feelings, peace, 
respect, all that ;-P.

Janice Caron wrote:
 Better, safer casts have been requested before - in fact I routinely
 see that request on the monthly "D wishlist" that goes round. 

I doubt any of the suggestions include "at the expense of the current cast". If all these were implemented, it would be a significant cognitive load on the programmer, and D is already a fairly complex language. Also, should getting rid of keywords really be a design goal? Only Walter (and you, apparently; no disrespect) seem to think so.
 But
 here's a fairly concrete suggestion as to what the syntax might be.
 And as a bonus, we'd get rid of the "cast" keyword, thus reducing the
 keyword count by one. So, here are the various types of transformation
 I suggest:
 
 
 (*) Build a new instance with a different memory layout, but with the
 same value. (For example, casting double to int). For lossless
 conversions, this can happen implicitly, but for lossy conversions, an
 explicit cast is necessary. For D, I suggest
 
     to!(T)(x)
 
 where to! is a template function provided by std.conv (not a keyword).
 to! already performs many conversions. Throwing in the basics like
 to!(int)(double) would be an obvious extension.

Ick! That means another identifier that needs to be a compiler built-in thus increasing coupling between the compiler and the standard library. Since it's not something the template can actually handle, why should it be in there rather a special construct in the core language?
 (*) Use RTTI to cast up and down the class heirarchy. In C++, this
 would be dynamic_cast<T>(x). For D, I suggest two things. Firstly, the
 cast:
 
     class!(T)(x)
 
 to do that actual upcast or downcast - but RTTI dynamic casts can
 fail, so the question arises: What should we do if the cast fails?
 Should we return null, or should we throw an exception. My view is
 that we should throw an exception, but also introduce a new construct
 to test whether or not the cast would be possible in the first place:
 
     is!(T)(x)
 
 which returns bool. is!(T) would be like Java's instanceof. Thus, one
 could write
 
     void f(A a)
     {
         if (is!(B)a)
         {
             B b = class!(B)(a);
             /*...*/
         }
         else if (is!(C)a)
         {
             C c = class!(C)(a);
             /*...*/
         }
     }
 
 etc.

But all this is possible with the current cast system. All you gain is a more complex syntax, and since "cast" is a term well-known to programmers, you lose a meaningful bit of syntax.
 (*) Reinterpret a bit pattern. In C++, this would be
 reinterpret_cast<T>(x). Without changing the memory layout, or the
 constancy, reinterpret the bits to mean something else. For D, I
 suggest
 
     union!(T)(x)

Ditto as above. Just means the person needs to think about something else. And why "union", that seems like an arbitrary keyword. WHy not "foreach_reverse!(T)(x)" which makes just as much sense.
 (*) Change const to mutable, or invariant to mutable. In C++, this
 would be const_cast<T>(x). There is no equivalent in D, however, in D,
 one can currently write cast(T)x, and constancy will be magically (and
 dangerously) waved away. In the new scheme, I suggest:
 
     auto!(T)(x)

How is that better than "cast(T) x;". I guess just because it creates the distinction?
 (*) Change const to invariant, or mutable to invariant. Currently, the
 syntax for this is cast(invariant)x, however this has two
 disadvantages: (i) the entire type is made invariant, and you might
 want to invariantize only part of the type, and (ii) cast(T)x also
 works. In other words, (i) is not powerful enough, and (ii) is too
 dangerous. There is no equivalent for this in C++, since C++ has no
 invariance. For D, I suggest:
 
     invariant!(T)(x)

Again, how is that any better? --- Okay, sorry that was so negative. But it just seems that cast() works perfectly fine for _all_ those things right now. If what you want is safer casting, you can write your own templates to do that.
May 09 2008
next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Robert Fraser wrote:
  [...]

Also, I find the cast-returns-null-on-failure behavior quite useful. In your system, this piece of code: Shape s = getShape(); if(auto r = cast(Rectangle) s) { return r.length * r.width; } else if(auto c = cast(Circle) s) { return PI * c.radius * c.radius; } ... would become ... Shape s = getShape(); if(is!(Rectangle)(s)) { auto r = class!(Rectangle) s; return r.length * r.width; } else if(is!(Rectangle)(s)) { auto c = class!(Circle) s; return PI * c.radius * c.radius; } Which is more typing and uglier IMO. Since if you want this behavior, you can already make templates to do it, why force it down people's throats?
May 09 2008
parent terranium <spam here.lot> writes:
Robert Fraser Wrote:

 Robert Fraser wrote:
  [...]

Also, I find the cast-returns-null-on-failure behavior quite useful. In your system, this piece of code: Shape s = getShape(); if(auto r = cast(Rectangle) s) { return r.length * r.width; } else if(auto c = cast(Circle) s) { return PI * c.radius * c.radius; } ... would become ... Shape s = getShape(); if(is!(Rectangle)(s)) { auto r = class!(Rectangle) s; return r.length * r.width; } else if(is!(Rectangle)(s)) { auto c = class!(Circle) s; return PI * c.radius * c.radius; }

... should become ... :)))) Shape s = getShape(); return s.square();
May 10 2008
prev sibling next sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 09 May 2008 19:58:49 +0100, Janice Caron <caron800 googlemail.co=
m>  =

wrote:

 On 09/05/2008, Robert Fraser <fraserofthenight gmail.com> wrote:
 I'm sorry if this post sounds argumentative, but I disagree strongly =


 with
 pretty much everything in this post. So no hard feelings, peace,  =


 respect,
 all that ;-P.

All is peaceful here on the newsgroup. This is just a nice, friendly chat. Nothing has been suggested "officially", and nothing will be, if=

 folk here don't like the idea.


    to!(T)(x)

 where to! is a template function provided by std.conv (not a keywor=



 to! already performs many conversions. Throwing in the basics like
 to!(int)(double) would be an obvious extension.

Ick! That means another identifier that needs to be a compiler built=


 Um. No. std.conv.to!(T) already exists.

 I guess what you're saying is, it's not possible to write a function
 like toInt(double) if the cast operator is gone. I hadn't thought of
 that. In that case, maybe it should be

     cast!(T) // lossy conversion
     to!(T) // conversion that always succeeds, or throws an exception


 And why "union", that seems like an arbitrary keyword.

It wasn't entirely arbitrary. A union /is/ a reinterpretation of bits.=

 It's one memory layout painted over another. (And it's a lot shorter
 than "reinterpret_cast").


    auto!(T)(x)

How is that better than "cast(T) x;". I guess just because it create=


 the
 distinction?

I guess I just couldn't think of anything better. :-) D doesn't have a=

 word for "mutable", and C++'s "const_cast" is too long. (And also
 misleading, since its primary use is to /remove/ constancy, not add
 it).


  Okay, sorry that was so negative. But it just seems that cast() work=


 perfectly fine for _all_ those things right now. If what you want is =


 safer
 casting, you can write your own templates to do that.

You can? I confess, my template writing skills aren't up to the job. If it could be done, that would be interesting. Obviously, you'd have to use non-reserved words for the names though. I'm curious to know if C++'s dynamic_cast<T>, const_cast<T> are implemented with templates. They certainly /look/ like C++ templates.

They look like templates deliberately because they do a similar job but are not (in any implementation I have seen). The same could be made true for any "cast!" magic. I think this is one place where breaking the rules is more acceptable. That said, I think the reason we still hav= e = C style casts in C++ is because they are the only way to implement cast template= s. = Probably in the mysts of time this was how it was done. Does anyone have a copy o= f "the design and evolution of C++" to hand? I bet the answer is in there.= On another note I like that reinterpret_cast and const_cast are hard to = = type. It slows you down and hopefully encourages you to think about why you mi= ght be doing something evil. Another poster's comment that reinterpret_casts are mostly to and from = void* hit home for me. I don't think this is as much of a problem when your hierarchy i= s = based on object though. I also don't like your choice of union. I can see the C = connection but it doesn't say re-interpret to me. Something more like good=3Dforce!(evil) = = appeals. When reviewing and correcting C++ code I make use of the _cast anchor as= = something (a code smell) to search for. This would be harder for single words. My = = language design axiom here would be longer names for more dangerous uses. Otherwise I like the proposal. I hope the discussion leads to something = = more concrete. Regards, Bruce.
May 09 2008
prev sibling parent terranium <spam here.lot> writes:
Robert Fraser Wrote:

 Also, should getting rid of keywords really be a design goal? Only 
 Walter (and you, apparently; no disrespect) seem to think so.

I agree here. I think that language should be simple and understandable, I see no difference between new construct and new keyword, if new construct is easy to understand, then it's good, but if it's not, no mysterious goal of minimizing number of keywords should be pursued at the cost of strange constructs.
May 10 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 09/05/2008, Robert Fraser <fraserofthenight gmail.com> wrote:
 I'm sorry if this post sounds argumentative, but I disagree strongly with
 pretty much everything in this post. So no hard feelings, peace, respect,
 all that ;-P.

All is peaceful here on the newsgroup. This is just a nice, friendly chat. Nothing has been suggested "officially", and nothing will be, if folk here don't like the idea.
    to!(T)(x)

 where to! is a template function provided by std.conv (not a keyword).
 to! already performs many conversions. Throwing in the basics like
 to!(int)(double) would be an obvious extension.

Ick! That means another identifier that needs to be a compiler built-in

Um. No. std.conv.to!(T) already exists. I guess what you're saying is, it's not possible to write a function like toInt(double) if the cast operator is gone. I hadn't thought of that. In that case, maybe it should be cast!(T) // lossy conversion to!(T) // conversion that always succeeds, or throws an exception
 And why "union", that seems like an arbitrary keyword.

It wasn't entirely arbitrary. A union /is/ a reinterpretation of bits. It's one memory layout painted over another. (And it's a lot shorter than "reinterpret_cast").
    auto!(T)(x)

How is that better than "cast(T) x;". I guess just because it creates the distinction?

I guess I just couldn't think of anything better. :-) D doesn't have a word for "mutable", and C++'s "const_cast" is too long. (And also misleading, since its primary use is to /remove/ constancy, not add it).
  Okay, sorry that was so negative. But it just seems that cast() works
 perfectly fine for _all_ those things right now. If what you want is safer
 casting, you can write your own templates to do that.

You can? I confess, my template writing skills aren't up to the job. If it could be done, that would be interesting. Obviously, you'd have to use non-reserved words for the names though. I'm curious to know if C++'s dynamic_cast<T>, const_cast<T> are implemented with templates. They certainly /look/ like C++ templates.
May 09 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 I too do not like the proposed syntax. I'd suggest the following scheme:
  1) convert: cast(foo)bar
  2) reinterpret: cast!(foo)bar
  3) constancy: cast(invariant), cast(mutable)
  4) downcast: cast!(foo)bar

Your proposal is better than mine, but I think I can improve it even more: 1) convert: cast(Foo)bar 2) reinterpret: cast!(Foo)bar 3) constancy: cast(invariant)bar, cast(!const)bar 4) downcast: cast(Foo)bar There are just two differences. I suggest cast(!const) to remove constancy, because cast(mutable) could be ambiguous if there happened to be a type called "mutable" (unless you're suggesting "mutable" be a reserved word, but Walter would never allow that). Downcasting isn't particularly dangerous, providing you always check the return value, so no exclamation mark needed there. The important point about this proposal is that the following will /not/ compile: const c = new C; auto d = cast(C)c; // ERROR - cast cannot remove constancy But this will const c = new C; auto d = cast(!const)c; // OK Likewise with downcasting void foo(in A a) { B b = cast(B)a; // ERROR - cast cannot remove constancy if (b !is null) ... } but the following would be OK void foo(in A a) { B b = cast(B)cast(!const)a; // OK if (b !is null) ... } although of course, what you probably should be doing is really: void foo(in A a) { const B b = cast(const B)a; // OK if (b !is null) ... }
May 09 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/05/2008, Dee Girl <deegirl noreply.com> wrote:
 With much interest I followed this thread. Forgive my ignorance, but I can not
understand one thing. Could this functionality be implemented as library
functions all using the built in cast?

Not really, since part of the proposal is that const c = new C; auto d = cast(C)c; // ERROR - cast cannot remove constancy should not compile. I see ruling this out as an important part of const correctness. If you make it optional (e.g. have some sort of template, static_cast!(T)), then people are just going to write "cast" instead of "static_cast!" because (a) it's shorter, and (b) the programmer's inherent belief that everything they write is bug free. That's exactly what happens in C++. Ruling out the ability of cast(T) to remove constancy is exactly the same philosophy as that of not allowing const objects to be passed to mutable functions. Naturally, there must be a way of removing constancy if you really, really want to, because D is a systems programming language. That's the reason Yigal suggested cast(mutable), and I suggested cast(!const). Either way, it tells the compiler "I'm doing this on purpose".
May 09 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/05/2008, Dee Girl <deegirl noreply.com> wrote:
 But in C++ I can not grep for old-style cast. In D I can.

That still doesn't help. You can stare at a bug for ages, and still not see it. But if the compiler tells you it's there, then you can slap your head and say "Doh! Of course! Why didn't I see that!?" The point being that it is way, way, /way/ better to catch bugs at compile-time, than at run-time. Any word for "catching bugs at run time" is "crashing". :-) We had exactly this situation recently with class C {} C c; if (c == null) // now a compile-time error. This was a run-time error for ages. ("c == anything" is translated to "c.opEquals(anything)", which will crash if c is unassigned. But the coder was attempting to test whether or not c was unassigned). Finally it's a compile-time error. Moving the bug detection to compile-time not only saved a lot of people a lot of hair-pulling out, it also exposed many bugs in Phobos. You can grep for "== null", but, so what? People don't. Sometimes its hard to see your own bugs. Having the compiler tell you that they're there is just fantastic. That's the reason for this proposal (the latest version of which is, I think, pretty reasonable). 1) convert or downcast: cast(Foo)bar 2) reinterpret: cast!(Foo)bar 3) constancy: cast(invariant)bar, cast(!const)bar
May 09 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  b) Downcasts - the danger with them is that they can fail if you
  downcast to the wrong derived class. that is why I've suggested:
 <snip>

Right. But in my view cast! (with an exclamation mark) can never fail as such, because it's the programmer telling the compiler, "Yes, I know you think this is wrong, but dammit I know what I'm doing, so deal with it!". It means that the compiler makes no checks, and trusts the programmer. (It's reinterpret_cast<T>). Therefore, it can never return null. But you are certainly correct in that there is a fundamental difference between static_cast<T> and dynamic_cast<T>. The former is a compile-time check, and the latter is a run-time check, so when the user types cast(T)x; the compiler does a compile-time check to determine whether the static type, typeof(x), could be downcast to T, and if so, generates code which performs a runtime check, which might return null. Although this works, the user is never entirely sure (a) whether the compiler has generated a straight conversion which will always succeed, or (b) whether the compiler has generated a runtime check, which is slower, and might fail. For that reason, there is merit in wanting an explicit dynamic_cast<T> equivalent. But it can't be cast!(T), because we've already used that for reinterpret_cast<T> - a cast which performs no checks at all, not even at compile time. Maybe we need a different syntax still for dynamic_cast<T>. One possibility (thought I don't recommend it) is: cast(class T)x The problem with that is that all the existing code which expects cast(T)x to be a dynamic cast will still compile, give the wrong result, and never return null. The change would introduce bugs, which is the exact opposite of the intent. For that reason, perhaps dynamic cast should be the default (so that existing code will still compile and give the /right/ result), with static_cast<T> having a different syntax. One really, really obvious syntax for static_cast<T>(x) is static cast(T)x which I think is pretty cool. It tells the compiler "I definitely want to bypass the run-time check, but I still want the compile-time check".
  "cast" can be defined by the user, but I'm not sure whether it would be
  a good idea to allow the same for "cast!"
  what do you think?

I think that cast! should mean reinterpret_cast<T>, and therefore always succeeds, bypassing all checks. Since it merely reinterprets the bits, it cannot be overloadable.
May 10 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  suppose the bit layout of a is illegal utf-32 encoding. would you prefer
  D allowed storing such an illegal value in a dchar?

I have to say yes. It's a question of levels. Higher level code should never store invalid UTF-32 in dchars nor dstrings. But lower level code must be able to work with them. For example, I wrote std.encoding. It has a function isValidCodePoint() which takes a dchar and tells you whether or not it contains a valid value. It also has a function, sanitize(), which takes possibly invalid UTF as input, and emits guaranteed valid UTF as output. It has a function, safeDecode(), which takes possibly invalid UTF as input, removes the first UTF sequence, regardless of whether valid or malformed, and returns either the decoded character or the constant INVALID_SEQUENCE, which is (cast(dchar)(-1)). The lowest level code has to be allowed, not merely /close/ to the metal, but to actually turn the nuts and bolts. That's what it means to be a systems programming language, and without that ability, no low level code could ever be written, without resorting to assembler. So yes, int n = anything; dchar c = cast!(dchar)n; must always succeed. The exclamation mark means "I know what I'm doing", which is exactly why it should be used with caution.
  IMO, a strongly typed language (like D) must enforce at all times that
  its variables are valid. I do not want D to allow storing illegal values
  like that. that must be an error.

Consider this: string s = "\u20AC"; /* s contains exactly one Unicode character */ string t = s[1..2]; Do you want to ban slicing? Do you want slicing always to invoke a call to std.encoding.isValid(), just to make sure the slice is valid? If so, you must see that std.encoding itself needs to be allowed to do low-level stuff. Higher level code is ultimately written in terms of lower level code, so you can't ban the lower level code. However, I would be more than happy with; int n; dchar c = cast(dchar)n; /* may throw */ dchar d = cast!(dchar)n; /* always succeeds */
May 10 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/05/2008, terranium <spam here.lot> wrote:
 Janice Caron Wrote:

  > Consider this:
  >
  >     string s = "\u20AC"; /* s contains exactly one Unicode character */
  >     string t = s[1..2];

 this makes string array of bytes rather than chars.

You are incorrect. Indisputably, typeof(t) == invariant(char)[]. It is an array of invariant chars - that is, an array of invariant UTF-8 code units. Each code unit is individually valid, but the complete string consists of two malformed code unit sequences, each of which is an isolated continuation byte. You are also missing the point. This thread is about casting, not Unicode. If you want to talk Unicode, I'm happy to do so, but please let's take that to another thread. I only brought up slicing as an example of why low level stuff must be permitted, and in specific response to a point made by Yigal.
May 10 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/05/2008, Janice Caron <caron800 googlemail.com> wrote:
 You are incorrect. Indisputably, typeof(t) == invariant(char)[]. It is
  an array of invariant chars - that is, an array of invariant UTF-8
  code units. Each code unit is individually valid, but the complete
  string consists of two malformed code unit sequences, each of which is
  an isolated continuation byte.

My apologies. The complete string consists of /one/ malformed code unit sequence, consisting of one isolated continuation byte, not two. For some reason I misread s[1..2] as s[1..3] (which is pretty dumb really, seeing as I wrote it). :-)
  If you want to talk Unicode, I'm happy to do so, but please
  let's take that to another thread.

This still stands.
May 10 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  a dchar (or [w]char) _must_ always contain valid data.

That has never been the case, ever. If you think that D should change, so as always to enforce Permanently Valid Unicode, then start a new thread and make a proposal. We'll discuss.
May 10 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 10/05/2008, Dee Girl <deegirl noreply.com> wrote:
 Nice example! How did you do it? Did Tango change the compiler and added more
methods to arrays? Thank you, Dee Girl

Yeah, but look what you can do with Phobos. import std.algorithm; int[] array = [ 1, 2, 3, 4 ]; // sort in descending order sort!("a > b")(array); assert(array == [ 4, 3, 2, 1 ]); // sort in ascending order sort(array); assert(array == [ 1, 2, 3, 4 ]); // sort with a delegate bool myComp(int x, int y) { return x > y; } sort!(myComp)(array); assert(array == [ 4, 3, 2, 1 ]); // Showcase stable sorting string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; sort!("toupper(a) < toupper(b)", SwapStrategy.stable)(words); assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);
May 10 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
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
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
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
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
 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.

Hmmm. Perhaps. But then you lose the distinction between const_cast and reinterpret_cast. With cast!() acting as reinterpret_cast, I can do: void foo(Something x) { auto y = cast!(Anything)x; ... } and it would always succeed, and you can't say "unless there's a change of constancy" in this case, because the before and after might have completely different memory layouts, so there'd be no way even to tell whether or not that had happened. I can see that const_cast and dynamic_cast could share the same syntax, but I really don't see how reinterpret_cast could double up with /anything/ else. So maybe that brings us to this: cast(T)x // dynamic_cast cast!(T)x // reinterpret_cast static cast(T)x // static_cast const cast(T)x // const_cast That does have a certain appeal to it.
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  > So maybe that brings us to this:
  >
  >     cast(T)x // dynamic_cast
  >     cast!(T)x // reinterpret_cast
  >     static cast(T)x // static_cast
  >     const cast(T)x // const_cast
  >
  > That does have a certain appeal to it.

  didn't you yourself complain that cast(T) can remove
  constancy by mistake and create bugs?

Yes, which is why I now suggest void foo(in C c) { C c = cast(C)c; /* ERROR - cast cannot remove constancy */ C d = const cast(C)c /* OK */ }
  why would you allow cast!(T) to do just that?

As in... void foo(in C c) { C c = cast!(C)c; /* OK */ } Because the exclamation mark means "allow anything" (but you're only ever reinterpreting bits, not assigning new ones). Reinterpret cast would most often be used for casting void* to T*, for some T, but it could just as easily be used to interpret T* as U*, or (for classes) C as D. Since the source type may have a completely different memory layout from the destination type, there is, in general, no way for the compiler to determine whether or not constancy has changed. (e.g. you could be casting from a pointer to an intptr_t, or vice versa). In short, this is the most dangerous form of cast of all, and should only ever be used when nothing else will work, and even then only if you're damn sure you need to circumvent the normal rules. (But since D is a systems programming language, this kind of super-dangerous casting must be allowed. It just shouldn't be the default. To my mind, the exclamation mark says that). Bear in mind that even without reinterpret cast, you will always be able to achieve the same thing with unions. Watch: DstType reinterpret_cast(DstType, SrcType)(SrcType x) { union U { SrcType src; DstType dst; } U u; u.src = x; return u.dst; } In other words, if you ban reinterpret cast, someone's only going to do it anyway. Ummm ... wait a minute ... ! I think I just found a reason /not/ to have a reinterpret cast in the language - the reason being, it can be done in a library function, as I just showed. Hey, I think that means we don't need that one at all. Hmm...
  Also, My definition was that cast() can change the memory while cast!()
  doesn't. it seems you define them in reverse of that.

Well, not quite. In C++, only static_cast<T>(x) can change the memory (but it might not), wheras I suggest that in D, cast(T)x (i.e. the default cast) should just "do the sensible thing" - sort of like it does now, except with the dangerous cases removed - so it would act like dynamic_cast<T>(x) where appropriate, and static_cast<T>(x) where dynamic_cast<T> would be inappropriate. So, sometimes it will change memory, sometimes not. But const cast and cast! would never change memory. Examples: double d; int x = cast(int)d; // creates new int class A {} class B : A {} A a = new B; B b = cast(B)a; // dynamic cast. a and b point to same object C b = static cast(B)a; // as above, but without the runtime check that a is really a B class D {} const d = new D; D d2 = cast(D)d; // ERROR - cannot remove constancy D d2 = const cast(D)d; // OK D d3 = cast(D)a; // ERROR - can't convert A to D D d3 = static cast(D)a; // ERROR - can't convert A to D D d3 = const cast(D)a; // ERROR - can't convert A to D D d3 = cast!(D)a; // OK - but you'd better know what you're doing!
May 11 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/05/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  if [re]interpret_cast can be implemented by the user and it is quite
  dangerous, than i think people who need that should implement that
  themselves with union as you showed. no need for D to provide that
  functionality by default.

Agreed. As a general principle, if it can be implemented in a library, then there's no need for new syntax. /However/ - it /should/ be implemented in a library, because we'd want to outlaw void* p; char* q = cast(char*) p; /* ERROR -- cannot cast from void* to char* */ reinterpret_cast should be the only kind of cast to allow this: char* q = reinterpret_cast!(char*)(p); /* OK */ [EDIT - see below]
  I'm still not sure about constancy casts. but in order to provide the
  minimal solution, I'd prefer cast!(T) to do constancy casts over adding
  another construct for that.

OK.
  also, I don't like the static cast(), and it
  might cause ambiguity problems. what is this good for?

There can't be any ambiguity problems (yet), because you can't (yet) call functions which take a "this" parameter at compile time. If you could, then "static cast(int)x", might invoke typeof(x)'s opCast() function, so arguably there might be ambiguity problems in the future -- if you're entering an obfuscated D contest! Let's ignore that for now. What's it good for? Performance. If B is a subclass of A, and I *know* that my A is actualy a B, static_cast<B> allows me avoid the runtime penalty that I'd get with dynamic_cast<B>. There would also be no need to check the return value, because static_cast always succeeds. Obviously, reinterpret_cast<B> will work everywhere that static_cast<B> works, but static_cast<B>(a) gets me a compile-time check that typeof(a) is a subclass or superclass of B, wheras with reinterpret_cast<B>(a) there's no compile-time checking at all. static_cast<T> can also do plain conversion, e.g. double d; int n = static_cast<int>(d); So, if the old-fashioned default cast were ever outlawed in C++, static_cast is the one you'd use to replace it for "plain" casts. However - given that we choose to define cast(T) to mean "if T is a class then use RTTI, otherwise don't", then, for non-classes, our cast(T) achieves what static_cast<T> does for C++. For classes ... well, who needs static cast anyway? Let's ditch it. So that leaves us with: cast(T)x // (T is a class) ? dynamic_cast : static_cast cast!(T)x // const_cast reinterpret_cast!(T)(x) // reinterpret_cast, implemented in library That pretty much covers it.
  open question: if this proposal does not provide a reinterpret_cast, can
  we split the cast!(T) to perform either a type change /or/ a constancy
  change?

I don't see how that could work in general. We could allow it for specific cases though - e.g casting from void* to T*, or from void[] to T[], etc. I think in those special cases, it could work. So, to turn an S* into a T* (where both are structs), you would do S* s = cast!(S*)cast(void*)t
  // below case uses the same bit pattern, exception on invalid double
  // value
  auto d2 = cast!(double)n;

I don't like that, and I see no need for it. reinterpret_cast!(double) can do that job. And there's no need for exception throwing - that would entail a runtime check, and at this level, we must assume the programmer knows what they're doing.
  same goes for classes with user defined cast and cast! operators.

There should never be any such thing as a user-defined cast! operator. It's meaningless. Look at it like this - C++ has four different kinds of cast, but only one kind of cast operator. It's all you need.
  downcast can be treated as a compiler pre-defined conversion from a
  class to its derived classes. uses the cast(Derived) form and throws
  exception on error.

It's not an error to fail an RTTI cast! e.g. void f(A a) { B1 b1 = cast(B1)a; if (b1 !is null) { ... } B2 b2 = cast(B2)a; if (b2 !is null) { ... } B3 b3 = cast(B3)a; if (b3 !is null) { ... } } So no need for throwing exceptions.
  constancy is performed via cast!(T).

OK.
  pointers are cast with cast!(T)

Only pointers to void.
  so to convert const(int)* to const(long*) you'll need something like:
  auto res = cast!(const(long*))cast(const(long)*)x;

I would have said cast!(const(long*))cast(const(void)*)x; or reinterpret_cast!(const(long*))x;
May 11 2008
prev sibling parent Leandro Lucarella <llucax gmail.com> writes:
Janice Caron, el  9 de mayo a las 09:15 me escribiste:
 (*) Use RTTI to cast up and down the class heirarchy. In C++, this
 would be dynamic_cast<T>(x). For D, I suggest two things. Firstly, the
 cast:
 
     class!(T)(x)
 
 to do that actual upcast or downcast - but RTTI dynamic casts can
 fail, so the question arises: What should we do if the cast fails?
 Should we return null, or should we throw an exception. My view is
 that we should throw an exception, but also introduce a new construct
 to test whether or not the cast would be possible in the first place:
 
     is!(T)(x)

I don't think it's a good idea, because you end up doing the RTTI check twice this way. I think class!(T)(x) (I don't like the name but that's another issue) should return null in case of failure. If you want it to throw an exception, just wrap it with enforce[1]: enforce!(class!(T)(x)) This have been discussed recently, but this way it's more efficient (and I think more clear and less typing): void f(A a) { if (B b = class!(B)(a)) { /*...*/ } else if (C c = class!(C)(a)) { /*...*/ } } [1] http://www.digitalmars.com/d/2.0/phobos/std_contracts.html
 (*) Reinterpret a bit pattern. In C++, this would be
 reinterpret_cast<T>(x). Without changing the memory layout, or the
 constancy, reinterpret the bits to mean something else. For D, I
 suggest
 
     union!(T)(x)
 
 
 (*) Change const to mutable, or invariant to mutable. In C++, this
 would be const_cast<T>(x). There is no equivalent in D, however, in D,
 one can currently write cast(T)x, and constancy will be magically (and
 dangerously) waved away. In the new scheme, I suggest:
 
     auto!(T)(x)

I think this 2 can be merged toguether and called horrible_cast in honour to Don Clugston's "Member Function Pointers and the Fastest Possible C++ Delegates" article [2] =) [2] http://www.codeproject.com/KB/cpp/FastDelegate.aspx -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- For me to ask a woman out, I've got to get into a mental state like the karate guys before they break the bricks. -- George Constanza
May 12 2008