www.digitalmars.com Home | Search | C & C++ | D | DMDScript | News Groups | index | prev | next
Archives

D Programming
D
D.gnu
digitalmars.D
digitalmars.D.bugs
digitalmars.D.dtl
digitalmars.D.ide
digitalmars.D.dwt
digitalmars.D.announce
digitalmars.D.learn
digitalmars.D.debugger

C/C++ Programming
c++
c++.announce
c++.atl
c++.beta
c++.chat
c++.command-line
c++.dos
c++.dos.16-bits
c++.dos.32-bits
c++.idde
c++.mfc
c++.rtl
c++.stl
c++.stl.hp
c++.stl.port
c++.stl.sgi
c++.stlsoft
c++.windows
c++.windows.16-bits
c++.windows.32-bits
c++.wxwindows

digitalmars.empire
digitalmars.DMDScript
electronics



digitalmars.D - Safer casts

↑ ↓ ← "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
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
↑ ↓ → 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
→ 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
→ terranium <spam here.lot> writes:
And I hate those exclamation marks and chains of brackets.
May 09 2008
→ "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
→ "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
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
↑ ↓ → 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
→ "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
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
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
↑ ↓ 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
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
↑ ↓ 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
↑ ↓ "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
→ 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
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
↑ ↓ 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
↑ ↓ 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
↑ ↓ 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
↑ ↓ → 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
"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
↑ ↓ "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
↑ ↓ → 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
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
↑ ↓ 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
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
↑ ↓ → 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
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
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
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
↑ ↓ 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
↑ ↓ 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
↑ ↓ 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
↑ ↓ 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
↑ ↓ 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
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
↑ ↓ 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
↑ ↓ 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
"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
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
→ 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
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
↑ ↓ 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
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
↑ ↓ 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
↑ ↓ → 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
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