View mode: basic / threaded / horizontal-split · Log in · Help
January 26, 2007
Re: Real World usage of D, Today (was Re: challenge #2: implement the varargs_reduce metafunction)
John Reimer wrote:
> On Wed, 24 Jan 2007 23:12:21 -0800, Andrei Alexandrescu (See Website For
> Email) wrote:
> 
>> Bill Baxter wrote:
>>> Well there is this issue that &foo where foo is an overloaded will give 
>>> you pointer to the first version of foo in the source file.  There's no 
>>> way to disambiguate as far as I know.  Particularly frustrating for 
>>> property methods
>>>
>>>    void prop(float x) { ... }
>>>    float prop() { ... }
>>>
>>>
>>> &prop just gives you the first one no matter what.
>>>
>>> Maybe not what you're talking about with "matching using constants" but 
>>> it is related to signature matching (or lack thereof).
>> Ouch. This warrants a bug report. It sure has ripples in lots of 
>> template code, too.
>>
>> Andrei
> 
> 
> That's not considered a bug, however.  There have been many complaints
> about D's signature lookup "protocol" over the last 2 to 3 years.  Walter
> has many times stated that it's done that way on purpose, I think to
> avoid compiler complexity. Maybe this example shows yet another reason why
> it hurts, but we've had no success convincing him otherwise.  That's just
> one example of "silent acception" of the symbol.  I'm actually surprised
> that Walter didn't invalidate that bug report.

I didn't invalidate it because it is a bug. The lookup thing was a 
debate about whether Java-style or C++-style overriding is done. D does 
C++ style overriding.
January 26, 2007
Re: Real World usage of D, Today (was Re: challenge #2: implement the varargs_reduce metafunction)
Andrei Alexandrescu (See Website For Email) wrote:
> kris wrote:
>> Andrei Alexandrescu (See Website For Email) wrote:
>> [snip]
...
>>> 3. I want to increase community's awareness of the importance of 
>>> those tests for larger use cases, as well as gather ideas from the 
>>> community about how to overcome D's shortcomings in passing the tests.
...
> That being said, my view on what should be fixed in D is a bit 
> different. For example, as I said, one of my main foci is putting a lead 
> sarcophagus around "inout" before its radioactive waste affects too much 
> code that will become broken later. Also, implicit conversion rules will 
> change, and again I want to get that in ASAP so not too much code 
> suffers. And so on.
...
> Andrei


Well, since you mention it... I'm not positive that I'm thinking of the 
same type problem that you are, but when writing templates in D, I've 
wanted something like storageof(T) capabilities, but the way I imagine 
it, the ideal solution would be a bit different.

(Actually, I'm not sure exactly what problem inout causes that you are 
referring to as requiring lead, but moving on..)

I would prefer for the types passed to a template to carry a lot more 
info across, and let the user decide which parts to throw away.  You 
could call this a "strict" template, if you want backward compatibility 
(but it's early in D's template design so maybe we don't care).

I'm thinking that this (current) syntax:

template foo(T) {
   T plus(T x, T y) { return x + y; }
}

would be equivalent to:

strict template foo(T) {
    T.value plus(T.value x, T.value y) { return x + y; }
}

In a 'strict' template the type "T" would carry a lot of baggage.  It 
would know if T was constant (as in a local "const int x = 10"), or a 
constant view (maybe) whether it is an lvalue, rvalue, literal, 
known-at-compile-time, static array, result of arithmetic, etc.

I couldn't tell you the actual names and definitions of all of these 
attributes -- I imagine they could be added one at a time as the need 
arose, starting with an equivalent for "storageof".  [OT: I personally 
don't like the name storageof() because 'storage class' doesn't connote 
'inout' versus 'in' to me, I don't really think of those as storage.  I 
guess it comes from the idea of storing const items in R/O memory?)

What we normally think of as the "type" is called T.value in a strict 
template.  If you use "T" you get a whole lot more -- in fact, we might 
want to force the user to say "T.all" in this case.

In my view, what we have now, is a leaky abstraction - you get some type 
info but not a lot.  When you pass T into a template, you lose bits and 
pieces of what makes T work.

There would be two ways to manage the template definition - one is to 
get out each piece of 'type modification' data that you want to copy to 
the new type, using something like:

If T is "const long" (but not lazy), then:

  int foo(T.constness T.lazyness T.value x)

(becomes:  int foo(const long x))

Alternately, you could let users specify things like "in T" and the "in" 
overrides the "inout" if there was one.

Kevin
January 26, 2007
Re: Real World usage of D, Today (was Re: challenge #2: implement the varargs_reduce metafunction)
Kevin Bealer wrote:
> I would prefer for the types passed to a template to carry a lot more 
> info across, and let the user decide which parts to throw away.  You 
> could call this a "strict" template, if you want backward compatibility 
> (but it's early in D's template design so maybe we don't care).
> 
> I'm thinking that this (current) syntax:
> 
> template foo(T) {
>    T plus(T x, T y) { return x + y; }
> }
> 
> would be equivalent to:
> 
> strict template foo(T) {
>     T.value plus(T.value x, T.value y) { return x + y; }
> }
> 
> In a 'strict' template the type "T" would carry a lot of baggage.  It 
> would know if T was constant (as in a local "const int x = 10"), or a 
> constant view (maybe) whether it is an lvalue, rvalue, literal, 
> known-at-compile-time, static array, result of arithmetic, etc.
> 
> I couldn't tell you the actual names and definitions of all of these 
> attributes -- I imagine they could be added one at a time as the need 
> arose, starting with an equivalent for "storageof".  
[snip]

These are great points. The direction I hope to steer things in would be 
to (backward-compatibly) continue matching types "lossily", but to also 
give you the ability to pattern-match the extra info when you want.

The syntax that I am proposing does away with storageof and is very much 
in spirit with the current D:

S int foo(S, T)(S T t) { }

It does exactly what it says it does, in a clear and terse manner, and 
is 100% within the spirit of existing D:

* It's customized on symbols S and T

* It's matching (by sheer position of S and T) the storage (i.e., all of 
the gooey fuzzy nice information about the argument passed) and the type 
of the incoming argument

* In this example it uses the storage in the result type (e.g. const 
goes to const)

* Inside the function can easily use either T separately or S T as a 
group that transports the storage around. You have total flexibility 
(and don't forget that juxtaposition is always easier than extraction).

So far we're only thinking of storage-like attributes, but a good deal 
of information can be encoded under the loose declaration of "storage".


Andrei
January 26, 2007
Re: Real World usage of D, Today (was Re: challenge #2: implement the varargs_reduce metafunction)
Andrei Alexandrescu (See Website For Email) wrote:
> The syntax that I am proposing does away with storageof and is very much 
> in spirit with the current D:
> 
> S int foo(S, T)(S T t) { }
> 
> It does exactly what it says it does, in a clear and terse manner, and 
> is 100% within the spirit of existing D:
> 
> * It's customized on symbols S and T
> 
> * It's matching (by sheer position of S and T) the storage (i.e., all of 
> the gooey fuzzy nice information about the argument passed) and the type 
> of the incoming argument
> 
> * In this example it uses the storage in the result type (e.g. const 
> goes to const)
> 
> * Inside the function can easily use either T separately or S T as a 
> group that transports the storage around. You have total flexibility 
> (and don't forget that juxtaposition is always easier than extraction).
> 
> So far we're only thinking of storage-like attributes, but a good deal 
> of information can be encoded under the loose declaration of "storage".

Would it also be possible to 'cherry-pick' attributes?
So that e.g something like S.constness expands to either 'const' or ''?

And would this mean 'raw' storage attributes would be valid template 
parameters? So that something like foo!(const) would be valid syntax?
January 26, 2007
Re: Real World usage of D, Today (was Re: challenge #2: implement the varargs_reduce metafunction)
Reply to Walter,

> I didn't invalidate it because it is a bug. The lookup thing was a
> debate about whether Java-style or C++-style overriding is done. D
> does C++ style overriding.
> 

Is it? VS complains if there is any question, I haven't checked GCC. Or are 
you talking about something different.
January 26, 2007
Re: Real World usage of D, Today (was Re: challenge #2: implement the varargs_reduce metafunction)
Andrei Alexandrescu (See Website For Email) wrote:
> Kevin Bealer wrote:
>> I would prefer for the types passed to a template to carry a lot more 
>> info across, and let the user decide which parts to throw away.  You 
>> could call this a "strict" template, if you want backward 
>> compatibility (but it's early in D's template design so maybe we don't 
>> care).
>>
>> I'm thinking that this (current) syntax:
>>
>> template foo(T) {
>>    T plus(T x, T y) { return x + y; }
>> }
>>
>> would be equivalent to:
>>
>> strict template foo(T) {
>>     T.value plus(T.value x, T.value y) { return x + y; }
>> }
>>
>> In a 'strict' template the type "T" would carry a lot of baggage.  It 
>> would know if T was constant (as in a local "const int x = 10"), or a 
>> constant view (maybe) whether it is an lvalue, rvalue, literal, 
>> known-at-compile-time, static array, result of arithmetic, etc.
>>
>> I couldn't tell you the actual names and definitions of all of these 
>> attributes -- I imagine they could be added one at a time as the need 
>> arose, starting with an equivalent for "storageof".  
> [snip]
> 
> These are great points. The direction I hope to steer things in would be 
> to (backward-compatibly) continue matching types "lossily", but to also 
> give you the ability to pattern-match the extra info when you want.
> 
> The syntax that I am proposing does away with storageof and is very much 
> in spirit with the current D:
> 
> S int foo(S, T)(S T t) { }

Interesting.  So I suppose I could explicitly instantiate the template as:

   int i;
   foo!(const)( i );

If so, this seems great.

> So far we're only thinking of storage-like attributes, but a good deal 
> of information can be encoded under the loose declaration of "storage".

Definitely.


Sean
January 26, 2007
Re: Real World usage of D, Today (was Re: challenge #2: implement the varargs_reduce metafunction)
Frits van Bommel wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> The syntax that I am proposing does away with storageof and is very 
>> much in spirit with the current D:
>>
>> S int foo(S, T)(S T t) { }
>>
>> It does exactly what it says it does, in a clear and terse manner, and 
>> is 100% within the spirit of existing D:
>>
>> * It's customized on symbols S and T
>>
>> * It's matching (by sheer position of S and T) the storage (i.e., all 
>> of the gooey fuzzy nice information about the argument passed) and the 
>> type of the incoming argument
>>
>> * In this example it uses the storage in the result type (e.g. const 
>> goes to const)
>>
>> * Inside the function can easily use either T separately or S T as a 
>> group that transports the storage around. You have total flexibility 
>> (and don't forget that juxtaposition is always easier than extraction).
>>
>> So far we're only thinking of storage-like attributes, but a good deal 
>> of information can be encoded under the loose declaration of "storage".
> 
> Would it also be possible to 'cherry-pick' attributes?
> So that e.g something like S.constness expands to either 'const' or ''?
> 
> And would this mean 'raw' storage attributes would be valid template 
> parameters? So that something like foo!(const) would be valid syntax?

This gets into something that would be nice to have in D: some sort of 
concept checking.  The easiest method would be something like this:

    alias Tuple!(int,char) A;
    void fnA( T in A )( T val ) {}

    void fnB( T : is( T == int ) || is( T == char ) )( T val ) {}

The ':' currently suggests type matching so perhaps the operator would 
need to be changes.  An alternate for the above which would work is:

    template fnC( T )
    in
    {
        this = is( T == int ) || is( T == char );
    }
    body
    {

    }

Which would be equivalent to:

    template fnC( T, bool match : true = Test!(T) )
    {

    }

    template Test( T )
    {
        const Test = is( T == int ) || is( T == char );
    }

The problem is that it's awkward to overload templates on concepts 
now--the C++ approach is just about the only way.  However, since 
templates can not be overloaded across module boundaries, this is also 
currently an option:

    template fnD( T )
    {
        static if( is( T == int ) || is( T == char ) )
        {
            ...
        }
        else
        {
            static assert( false );
        }
    }

It works just fine, but requires all overloads to exist within the same 
template block, and this isn't terribly readable with a large number of 
conditions.


Sean
January 26, 2007
Re: Real World usage of D, Today (was Re: challenge #2: implement the varargs_reduce metafunction)
BCS wrote:
> Reply to Walter,
> 
>> I didn't invalidate it because it is a bug. The lookup thing was a
>> debate about whether Java-style or C++-style overriding is done. D
>> does C++ style overriding.
>>
> 
> Is it? VS complains if there is any question, I haven't checked GCC. Or 
> are you talking about something different.

Looks like something different.
January 26, 2007
Re: Real World usage of D, Today (was Re: challenge #2: implement the varargs_reduce metafunction)
Frits van Bommel wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> The syntax that I am proposing does away with storageof and is very 
>> much in spirit with the current D:
>>
>> S int foo(S, T)(S T t) { }
>>
>> It does exactly what it says it does, in a clear and terse manner, and 
>> is 100% within the spirit of existing D:
>>
>> * It's customized on symbols S and T
>>
>> * It's matching (by sheer position of S and T) the storage (i.e., all 
>> of the gooey fuzzy nice information about the argument passed) and the 
>> type of the incoming argument
>>
>> * In this example it uses the storage in the result type (e.g. const 
>> goes to const)
>>
>> * Inside the function can easily use either T separately or S T as a 
>> group that transports the storage around. You have total flexibility 
>> (and don't forget that juxtaposition is always easier than extraction).
>>
>> So far we're only thinking of storage-like attributes, but a good deal 
>> of information can be encoded under the loose declaration of "storage".
> 
> Would it also be possible to 'cherry-pick' attributes?
> So that e.g something like S.constness expands to either 'const' or ''?
> 
> And would this mean 'raw' storage attributes would be valid template 
> parameters? So that something like foo!(const) would be valid syntax?

You will be able to cherry-pick with "is" tests. Walter is opposed to 
manipulating the raw storage attributes.

Andrei
January 27, 2007
Re: Issues with constants, and inout (was Real World usage of D, Today)
kris wrote:
[about implicit conversion rules]
> extern (C) int printf (char*, ...);
> 
> class Foo
> {
>         void write (int x) {printf("int\n");}
>         void write (uint x) {printf("uint\n");}
>         void write (long x) {printf("long\n");}
>         void write (char x) {printf("char\n");}
>         void write (wchar x) {printf("wchar\n");}
>         void write (double x) {printf("double\n");}
> 
>         void write (char[] x) {printf("char[]\n");}
>         void write (wchar[] x) {printf("wchar[]\n");}
> }
> 
> void main()
> {
>         auto foo = new Foo;
> 
>         foo.write ('c');
>         foo.write (1);
>         foo.write (1u);
>         foo.write (3.14);
>         //foo.write ("asa");
> }
> 
> prints:
> 
> char
> int
> uint
> double
> 
> 
> DMD has actually become smarter than the last time I tried something 
> like this: it manages to select the correct overload for 'c' whereas 
> before it couldn't decide whether int or uint was a better match for the 
> char instead. This is good.
> It seems clear from the above that D /defaults/ the type of character, 
> and undecorated integers, to something appropriate? In the above case 
> 'c' is defaulted to char, rather than wchar, for example. The 
> undecorated int constant is defaulted to int, rather than uint or long. 
> This is good.

Yup. So far so good.

> Now for the broken part. When you uncomment the string constant, the 
> compiler gets all confused about whether it's a char[] or wchar[]. There 
> is no defaulting to one type, as there is for other constants (such as 
> char). It /is/ possible to decorate the string constant in a similar 
> manner to decorating integer constants:
> 
> foo.write ("qwe"c);
> 
> And this, of course, compiles. It's a PITA though, and differs from the 
> rules for other constants.

I talked to Walter about this and it's not a bug, it's a feature :o). 
Basically it's hard to decide what to do with an unadorned string when 
both wchar[] and char[] would want to "attract" it. I understand you're 
leaning towards defaulting to char[]? Then probably others will be unhappy.

> Things start to go south when using templates 
> with string constants. For example, take this template sig:
> 
> uint locate(T) (T[] source, T match, uint start=0)
> 
> This is intended to handle types of char[], wchar[] and dchar[]. There's 
> a uint on the end, as opposed to an int. Suppose I call it like this:
> 
> locate ("abc", "ab", 1);
> 
> we get a compile error, since the int-constant does not match a uint in 
> the sig (IFTI currently needs exact sig matches). In order to get around 
> this, we wrap the template with a few functions:
> 
> uint locate (char[] source, char[] match, uint start=0)
> {
>     return locateT!(char) (source, match, start);
> }
> 
> uint locate (wchar[] source, wchar[] match, uint start=0)
> {
>     return locateT!(wchar) (source, match, start);
> }
> 
> and dchar too.
> 
> Now we call it:
> 
> locate ("abc", "ab", 1);
> 
> Well, the int/uint error goes away (since function matching operates 
> differently than IFTI matching), but we've now got our old friend back 
> again -- the constant char[], wchar[], dchar[] mismatch problem.

I think a sound solution to this should be found. It's kind of hard, 
because char[] is the worst match but also probably the most used one. 
The most generous match is dchar[] but wastes much time and space for 
the minority of cases in which it's useful.

> How about another type of template? Here's one that does some simply 
> text processing:
> 
> T[] layout(T) (T[] output, T[] format, T[][] subs...)
> 
> This has an output buffer, a format string, and a set of optional args; 
> all of the same type. If I call it like so:
> 
> char[128] tmp;
> char[]    world = "world";
> layout (tmp, "hello %1", world);
> 
> that compiles ok. If I use wchar[] instead, it doesn't compile:
> 
> wchar[128] tmp;
> wchar[]    world = "world";
> layout (tmp, "hello %1", world);
> 
> In this case, the constant string used for formatting remains as a 
> char[], so the template match fails (args: wchar[], char[], wchar[])
> 
> However, if I change the template signature to this instead:
> 
> T[] layout(T) (T[] output, T[][] subs...)
> 
> then everything works the way I want it to, but the design is actually 
> wrong (the format string is now not required). String constants can be a 
> royal PITA.

Color me convinced. :o) I have no bright ideas on solving it though.

> inout
> -----
> 
> Since you're working on inout also, I'd like to ask what the plan is 
> relating to a couple of examples. Tango uses this style of call quite 
> regularly:
> 
> get (inout int x);
> get (inout char[] x);
> etc.
> 
> This is a clean way to pass-by-reference, instead of dealing with all 
> the pointer syntax. I sure hope D will retain reference semantics like 
> this, in some form?

It will, and in the same form.

> One current problem with inout, which you might not be aware of, is with 
> regard to const structs. I need to pass structs by reference, because I 
> don't want to pass them by value. Applying inout is the mechanism for 
> describing this:
> 
> struct Bar {int a, b;}
> 
> Bar b = {1, 2};
> 
> void parf (inout Bar x) {}
> 
> void main()
> {
>    parf (b);
> }
> 
> That all works fine. However, when I want to make those structs /const/ 
> instead, I cannot use inout since it has mutating semantics: I get a 
> compile error to that effect:
> 
> const Bar b = {1, 2};
> 
>  >> Error: cannot modify const variable 'b'
> 
> That is, there's no supported way to pass a const struct by reference. 
> The response from Walter in the past has been "just use a pointer 
> instead" ... well, yes I could do that. But it appears to be indicative 
> of a problem with the language design?

This case is on the list. You will definitely have a sane and simple way 
to pass const structs by reference, while having a guarantee that they 
can't be changed by the callee.

> Why do I want to use const? Well, the data held therein is for reference 
> only, and (via some future D vendor) I want that reference data placed 
> into a ROM segment. I do a lot of work with MCUs, and this sort of thing 
> is a common requirement.

I agree.


Andrei
1 2 3 4 5 6 7
Top | Discussion index | About this forum | D home