December 15, 2006
Lionello Lunesu wrote:
> Justin C Calvarese wrote:
>> Chris Nicholson-Sauls wrote:
>>> Lionello Lunesu wrote:
>>>> "Alexander Panek" <a.panek@brainsware.org> wrote in message news:eln5td$2pta$1@digitaldaemon.com...
>>>>> Are you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Or do you mean something /similar/ to C# + .NET? If so, I agree. Something like that has to be in D. :) I hope this will pop up
>>>>
>>>> We had one, once : ( but then the guy's HDD crashed... Sad, sad story.
>>>>
>>>> L.
>>>>
>>>
>>> https://mywebspace.wisc.edu/daaugustine/web/d/
>>>
>>> This is all that remains of all his work, so far as I can tell.  Some day it would be nice if http://www.scratch-ware.net/dfiles/ suddenly existed again.  Oh well.
>>>
>>> -- Chris Nicholson-Sauls
>>
>> I think I have pretty much all of the binary files he released (though they're archived on some CD in my closet), but he never released any source before his hard drive crashed, so I don't have any of that. And I think the binaries targeted some beta version of .NET and required .dll's that you'd only have if you installed a beta version of Visual Studio.
> 
> Can't assemblies be fairly easily disassembled?

Yes.  In fact, the times I've done it I've seen an exact copy of the original source code (!).  Try Reflector from this link:

http://www.aisto.com/roeder/dotnet/


Sean
December 15, 2006
== Quote from Walter Bright (newshound@digitalmars.com)'s article
> Andrei Alexandrescu (See Website For Email) wrote:
> > I see this as D's #1 major problem as of today. I think the function returning inout is better, but I foresee a bunch of issues with it. The question whether lvalues are needed beyond function return values must be satisfactorily answered.
>
> Ok, I think I understand the issue now. Also, it only applies to struct types, not class types. Class types are already reference types, so for them it's a non-issue.

In the case of opIndex(), I think it does affect classes too.  If I do this:

void Swap(inout T a, inout T b)
{
    T c=a; a=b; b=c;
}

class Vector { ... };
Vector X;

1. X[i].increment();
2. Swap(X[i], X[j]);

Both #1 and #2 would work for class or struct if X is a regular D array.  For user-defined types, both of these fail if X is a struct.  For classes, #1 works but I think #2 still fails.

Kevin
December 15, 2006
Kevin Bealer wrote:
> == Quote from Andrei Alexandrescu (See Website For Email)
> (SeeWebsiteForEmail@erdani.org)'s article
>> ++s[a]; // possible through a compiler hack
>> ++s(a, b); // impossible
> 
> I missed this possibility.  In order to solve just this next step { ++s(a, b); },
> it seems like D would need something that copies like a pointer to either a struct
> or class reference, but automatically turns into (or acts like) a regular
> reference or LValue when something like ++ is applied.  This means something in
> between a pointer and an LValue.

I think it's an lvalue all right. Inside the compiler it's very clear
what's an lvalue and what's not. Think of this:

int x;

Wherever you use x, even if you pass it to functions etc., the compiler
entirely knows it's an lvalue. If, on the other hand, you use (x + 1),
all of a sudden the compiler understands that's an rvalue, and
accordingly won't let you e.g. pass it as an out or inout parameter.

I was quite worried last night about this issue, but now I think I
realize that returning from a function is about the only (I hope!) case
in which the lvalue yes/no information about a value is lost. That is,
you can't today write a function ident() that is totally transparent -
i.e., can be added around any expression with no effect whatsoever:

template ident(T) {
  T ident(T rvalue) { // works
    return rvalue;
  }
  T ident(inout T lvalue) { // loss of information
    return lvalue;
  }
}

By the way, ident() is a good function to check a language's quality. If
it can't be implemented at all or efficiently, then the language is not
ideal. Most languages can implement ident() but not efficiently. C++
came close to implementing it properly, but all the confusion between
rvalues and const references made implementation exceedingly difficult
(to add insult to injury, built-in rvalues are treated differently than
user-defined rvalues). Current D cannot implement ident at all. I also
wonder whether overloading on inout is possible. I think not, and if
not, that is a good urgent item to put on the list. The code that should
compile and execute correctly is:

template ident(T) {
  T ident(T rvalue) { // works
    return rvalue;
  }
  inout T ident(inout T lvalue) { // works
    return lvalue;
  }
}

> I kind of hate to say it, but given all this, how far am I from describing a C++
> "&" type?   Is there anything the C++ type does that isn't in the above list?
> (Other than the new proposal for '& &' references I guess, which seems unnecessary
> for D.)

There are a few differences that make all the difference :o). One is
that C++ is too eager to convert rvalues to const &, which has caused an
unbounded amount of harm. Second, in C++, T& is a type indeed, but it's
a half-life type, a pariah. So it would be probably wise to do the
entire lvalue/rvalue distinction without making references into types.
For example, outside function declarations, there should be no other
place where inout can be used.


Andrei
December 15, 2006
Andrei Alexandrescu (See Website For Email) wrote:
> Kevin Bealer wrote:
> 
>> I kind of hate to say it, but given all this, how far am I from describing a C++
>> "&" type?   Is there anything the C++ type does that isn't in the above list?
>> (Other than the new proposal for '& &' references I guess, which seems unnecessary
>> for D.)
> 
> There are a few differences that make all the difference :o). One is
> that C++ is too eager to convert rvalues to const &, which has caused an
> unbounded amount of harm. Second, in C++, T& is a type indeed, but it's
> a half-life type, a pariah. So it would be probably wise to do the
> entire lvalue/rvalue distinction without making references into types.
> For example, outside function declarations, there should be no other
> place where inout can be used.

Agreed.  In my time using D, the only time I've wished for a reference type is for function return values.  The language doesn't need a general reference qualifier.  Besides, we've already got inout parameter types, and it seems completely reasonable that one should be able to pass references out of a function as well as into a function.


Sean
December 15, 2006
Walter Bright wrote:
> Stewart Gordon wrote:
>> We already have what strikes the rest of us as a compelling case: what we've just told you that returning from opAssign _should_ be able to do.
> 
> That case was trying to make opAssign be a constructor. Since there are other ways to construct it, I don't see why it's a compelling case. What is bought with this that cannot be otherwise done?

The cases you seemingly pander to for having opAssign in the first place are trying to make opAssign be a property setter.  Since there are other ways to set properties, I don't see why this is a compelling case.  What is bought with this that cannot be otherwise done?

Stewart.
December 15, 2006
== Quote from Andrei Alexandrescu (See Website For Email)
(SeeWebsiteForEmail@erdani.org)'s article
> Kevin Bealer wrote:
> > == Quote from Andrei Alexandrescu (See Website For Email)
> > (SeeWebsiteForEmail@erdani.org)'s article
> >> ++s[a]; // possible through a compiler hack
> >> ++s(a, b); // impossible
> >
> > I missed this possibility.  In order to solve just this next step { ++s(a, b); }, it seems like D would need something that copies like a pointer to either a struct or class reference, but automatically turns into (or acts like) a regular reference or LValue when something like ++ is applied.  This means something in between a pointer and an LValue.
>
> I think it's an lvalue all right. Inside the compiler it's very clear what's an lvalue and what's not. Think of this:
>
> int x;
>
> Wherever you use x, even if you pass it to functions etc., the compiler
> entirely knows it's an lvalue. If, on the other hand, you use (x + 1),
> all of a sudden the compiler understands that's an rvalue, and
> accordingly won't let you e.g. pass it as an out or inout parameter.
> I was quite worried last night about this issue, but now I think I
> realize that returning from a function is about the only (I hope!) case
> in which the lvalue yes/no information about a value is lost. That is,
> you can't today write a function ident() that is totally transparent -
> i.e., can be added around any expression with no effect whatsoever:
>
> template ident(T) {
>    T ident(T rvalue) { // works
>      return rvalue;
>    }
>    T ident(inout T lvalue) { // loss of information
>      return lvalue;
>    }
> }
> By the way, ident() is a good function to check a language's quality. If
> it can't be implemented at all or efficiently, then the language is not
> ideal. Most languages can implement ident() but not efficiently. C++
> came close to implementing it properly, but all the confusion between
> rvalues and const references made implementation exceedingly difficult
> (to add insult to injury, built-in rvalues are treated differently than
> user-defined rvalues). Current D cannot implement ident at all. I also
> wonder whether overloading on inout is possible. I think not, and if
> not, that is a good urgent item to put on the list. The code that should
> compile and execute correctly is:
>
> template ident(T) {
>    T ident(T rvalue) { // works
>      return rvalue;
>    }
>    inout T ident(inout T lvalue) { // works
>      return lvalue;
>    }
> }
>
> > I kind of hate to say it, but given all this, how far am I from describing a C++ "&" type?   Is there anything the C++ type does that isn't in the above list? (Other than the new proposal for '& &' references I guess, which seems unnecessary for D.)
>
> There are a few differences that make all the difference :o). One is
> that C++ is too eager to convert rvalues to const &, which has caused an
> unbounded amount of harm. Second, in C++, T& is a type indeed, but it's
> a half-life type, a pariah. So it would be probably wise to do the
> entire lvalue/rvalue distinction without making references into types.
> For example, outside function declarations, there should be no other
> place where inout can be used.
> Andrei

This is really interesting stuff.  You've probably seen these already but lately D has gotten some compile time introspection stuff via tuples, in particular I'm thinking of (http://www.digitalmars.com/d/tuple.html) and (http://www.digitalmars.com/d/phobos/std_traits.html), which I think look quite useful.

So I'm imagining a case of "meta-ident", which is to say, a template cousin of the ident test, applying the 'wrapper' concept to metaprogramming instead of programming.  Knowing only the name of a method of a parameter class method (T::run), I want to duplicate it's signature in a template class method (A::meta).

template A(T) {
    class A {
        T wrapped;

        this(T w) {
            wrapped = w;
        }

        ReturnType!(T.run) meta1(ParameterType!(T.run) i)
        {
            return wrapped.run(i);
        }

        inout ReturnType!(T.run) meta2(inout ParameterType!(T.run) i)
        {
            return wrapped.run(i);
        }
    }
}

Looking just at meta1, I'm wondering if it can absorbs the LValue-ness (inout qualification) through the Tuple.  I'm thinking that it can't.  If the LValue return type concept only affects function signatures as you say, then it can't be stored in a tuple, right?  Instead it would get the type minus the inout qualifier?

Now consider these two definitions of T::run():

struct S {...}

      S T::run1(in    S x);
inout S T::run2(inout S x);

If I want to write a perfect-forwarding wrapper for run2, I need to use something
like meta2, so that the LValue-ness is preserved.  But meta2 can't work with run1,
because the local S returned by run1 can't be returned via an inout return value.
 It would be returning a ref to a local var.  So I need to know if the function
takes and returns out vs. inout, in order to wrap it up like meta() is trying to do.

This looks a bit like the C++ problem of having to define const and non-const versions of all the operators, except that meta1 or meta2 will do the wrong thing *silently* in these cases, either returning references to local vars (meta2 + run1), or discarding LValue-ness (meta1 + run2).

Maybe I'm solving a non-problem here: in most cases the user should know if
T::run() returns an LValue or RValue; for example, opIndex() could be understood
to always return an LValue by convention.

It seems like this local case could be solved by making ReturnType!() and ParameterTypeTuple!() into language attributes, so that they could see the fully qualified function-signature grade types with passing conventions etc.

  T::run.return_type_of  meta_all(T::run.parameter_type_of[0..$] x)
  {
      // pass all the parameters via some kind of compiler-aware tuple
      return wrapped.run(x[0..$]);
  }

Which could presumably, provide perfect forwarding of T::run() when used in a function signature context.

[Or maybe there's always a conceptual 'leak' in these kinds of systems, and it can
only be pushed a finite distance toward obscurity with each new language feature.
 I hope not, but...]

Kevin
December 16, 2006
Walter Bright wrote:
> More ABI changes, and implicit [] => * no longer allowed.
> 
> http://www.digitalmars.com/d/changelog.html
> 
> http://ftp.digitalmars.com/dmd.175.zip

Looks like casts from void* to struct* is broken.

Russ
December 16, 2006
Kevin Bealer wrote:
> This is really interesting stuff.  You've probably seen these already but lately D
> has gotten some compile time introspection stuff via tuples, in particular I'm
> thinking of (http://www.digitalmars.com/d/tuple.html) and
> (http://www.digitalmars.com/d/phobos/std_traits.html), which I think look quite
> useful.

I'm familiar with those :o).

> So I'm imagining a case of "meta-ident", which is to say, a template cousin of the
> ident test, applying the 'wrapper' concept to metaprogramming instead of
> programming.  Knowing only the name of a method of a parameter class method
> (T::run), I want to duplicate it's signature in a template class method (A::meta).
[snip]
> Looking just at meta1, I'm wondering if it can absorbs the LValue-ness (inout
> qualification) through the Tuple.  I'm thinking that it can't.  If the LValue
> return type concept only affects function signatures as you say, then it can't be
> stored in a tuple, right?  Instead it would get the type minus the inout qualifier?

Your observation is correct. It all boils down to this: if "inout" is not part of the type system, there are two issues: (1) two overloads will always be needed to catch it (the similar way it's needed to overload the same function on const/non-const), and also (2) compile-time introspection would be more complicated as you need to "catch" the inout part of the parameter separately - the type won't be part of it.

> It seems like this local case could be solved by making ReturnType!() and
> ParameterTypeTuple!() into language attributes, so that they could see the fully
> qualified function-signature grade types with passing conventions etc.
> 
>   T::run.return_type_of  meta_all(T::run.parameter_type_of[0..$] x)
>   {
>       // pass all the parameters via some kind of compiler-aware tuple
>       return wrapped.run(x[0..$]);
>   }
> 
> Which could presumably, provide perfect forwarding of T::run() when used in a
> function signature context.
> 
> [Or maybe there's always a conceptual 'leak' in these kinds of systems, and it can
> only be pushed a finite distance toward obscurity with each new language feature.
>  I hope not, but...]

That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow.


Andrei
December 16, 2006
Andrei Alexandrescu (See Website For Email) wrote:
> Kevin Bealer wrote:
> 
>> This is really interesting stuff.  You've probably seen these already but lately D has gotten some compile time introspection stuff via tuples, in particular I'm thinking of 
>> 
>> (http://www.digitalmars.com/d/tuple.html) and
>> (http://www.digitalmars.com/d/phobos/std_traits.html), 
>>
>> which I think look quite useful.
> 
> I'm familiar with those :o).

LOL!

Incidentally, the technical expertise, theoretical depth, and rigor of thinking that you've brought with you, have been sorely missed here.

We'd be nowhere near this far without your already big contributions to D! I can hardly imagine what we've got ahead of us!
December 16, 2006
== Quote from Andrei Alexandrescu (See Website For Email)
(SeeWebsiteForEmail@erdani.org)'s article
> Kevin Bealer wrote:
> > This is really interesting stuff.  You've probably seen these already but lately D has gotten some compile time introspection stuff via tuples, in particular I'm thinking of (http://www.digitalmars.com/d/tuple.html) and (http://www.digitalmars.com/d/phobos/std_traits.html), which I think look quite useful.
>
> I'm familiar with those :o).

Yes, I have this red covered book around here that goes into more depth, but I can never find it... :)

> > So I'm imagining a case of "meta-ident", which is to say, a template cousin of the ident test, applying the 'wrapper' concept to metaprogramming instead of programming.  Knowing only the name of a method of a parameter class method (T::run), I want to duplicate it's signature in a template class method (A::meta).
> [snip]
> > Looking just at meta1, I'm wondering if it can absorbs the LValue-ness (inout qualification) through the Tuple.  I'm thinking that it can't.  If the LValue return type concept only affects function signatures as you say, then it can't be stored in a tuple, right?  Instead it would get the type minus the inout
qualifier?
>
> Your observation is correct. It all boils down to this: if "inout" is not part of the type system, there are two issues: (1) two overloads will always be needed to catch it (the similar way it's needed to overload the same function on const/non-const), and also (2) compile-time introspection would be more complicated as you need to "catch" the inout part of the parameter separately - the type won't be part of it.
>
> > It seems like this local case could be solved by making ReturnType!() and ParameterTypeTuple!() into language attributes, so that they could see the fully qualified function-signature grade types with passing conventions etc.
> >
> >   T::run.return_type_of  meta_all(T::run.parameter_type_of[0..$] x)
> >   {
> >       // pass all the parameters via some kind of compiler-aware tuple
> >       return wrapped.run(x[0..$]);
> >   }
> >
> > Which could presumably, provide perfect forwarding of T::run() when used in a function signature context.
> >
> > [Or maybe there's always a conceptual 'leak' in these kinds of systems, and it can
> > only be pushed a finite distance toward obscurity with each new language feature.
> >  I hope not, but...]
>
> That remains to be seen, but I think the buck stops at functions. The problem of duplicating templates for inout (lvalues) and rvalues stays, but I have an idea about that, that I might tell about tomorrow.
>
> Andrei

That hint got me thinking and I had a look for myself at whether the current system can be used to make the distinction between in and inout.  It took me a while to nail down a syntax that could do it.  Of course, I can't test it with "inout" return types since we don't have those to play with, but I think what I have here might work for them if they were available.  (Maybe inout is a bad name for a return type, but...)

This is what I was able to make:

import std.stdio;
import std.traits;

class Refer {
    alias int Item;

    Item run(inout Item x)
    {
        writefln("run(io)");
        x += 300;
        return 10;
    }
};

class Value {
    alias int Item;

    Item run(Item x)
    {
        writefln("run(i)");
        x += 400;
        return 20;
    }
};

template Baz(T) {
    class Baz {
        T wr_;

        this()
        {
            wr_ = new T();
        }

        alias T.Item Item;
        alias Item delegate(inout Item) run_IO;
        alias Item delegate(Item)       run_I;

        alias typeof(& (new T).run) TRunFunc;

        static if (is(typeof(TRunFunc) == typeof(run_IO))) {
            Item walk(inout Item x)
            {
                writefln("walk(io)");
                x += 5000;
                return wr_.run(x);
            }
        }
        static if (is(typeof(TRunFunc) == typeof(run_I))) {
            Item walk(Item x)
            {
                writefln("walk(i)");
                x += 6000;
                return wr_.run(x);
            }
        }
    }
}

int main(char[][] args)
{
    alias Baz!(Value) TValue;
    alias Baz!(Refer) TRefer;

    TValue v = new TValue;
    TRefer r = new TRefer;

    int a = 0;
    int b = v.walk(a);

    int c = 0;
    int d = r.walk(c);

    writefln("\nValue semantics %s, %s.", a, b);
    writefln("Refer semantics %s, %s.", c, d);

    return 0;
}

Output looks like:

walk(i)
run(i)
walk(io)
run(io)

Value semantics 0, 20.
Refer semantics 5300, 10.

So, I can match and select the preferred signature via delegates and typeof etc. Of course, if everyone uses this, it would be good to have some kind of shortcut for the above syntax.

Maybe the IsExpression could be expanded to accept types like this:

 is(T.run() : int function(inout int))

Right now this is accepted (compiles) but does not seem to work - it always seems to evaluate as false.

Kevin