Thread overview
Re: Extended Type Design.
Mar 17, 2007
Reiner Pope
Mar 17, 2007
Reiner Pope
Mar 17, 2007
Reiner Pope
Mar 17, 2007
Reiner Pope
March 17, 2007
Andrei Alexandrescu (See Website For Email) Wrote:

> 
> We have talked about a design. In short, the intent is to define three flavors of immutability:
> 
> a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se;
> 
> b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively);
> 
> c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable.
> 
Is there anything in your discussion about compile-time constants (the sort that you need for 'static if', etc) ? You spoke a while ago about improving compile-time evaluation syntax so that there would be no difference at the call  site between runtime and compile-time evaluation (like for regexps).

It seems like you could get this quite easily by adding just one more parameter modifier (is this a storage class? I'm not sure) to denote, "any time this function is called, this parameter must be known at compile-time". Effectively, it would just be a conversion from this:

RegExpMatch match(static const char[] pattern, char[] text) {...}
...
main()
{
     match("gr[ae]y", din.read());
}

to this:

RegExpMatch match(char[] pattern)(char[] text) {...}
...
main()
{
     match!("gr[ae]y")(din.read());
}

Any plans this way?

Cheers,

Reiner
March 17, 2007
Reiner Pope wrote:
> Andrei Alexandrescu (See Website For Email) Wrote:
> 
>> We have talked about a design. In short, the intent is to define three flavors of immutability:
>>
>> a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se;
>>
>> b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively);
>>
>> c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable.
>>
> Is there anything in your discussion about compile-time constants (the sort that you need for 'static if', etc) ? You spoke a while ago about improving compile-time evaluation syntax so that there would be no difference at the call  site between runtime and compile-time evaluation (like for regexps).

I understand this is high on Walter's list. The syntax discussed is:

// dispatched when the pattern is known at compile time
RegExpMatch match(const char[] p)(const char[] p, char[] text) {...}
// dispatched when the pattern is a runtime string
RegExpMatch match()(const char[] p, char[] text) {...}

> It seems like you could get this quite easily by adding just one more parameter modifier (is this a storage class? I'm not sure) to denote, "any time this function is called, this parameter must be known at compile-time". Effectively, it would just be a conversion from this:
> 
> RegExpMatch match(static const char[] pattern, char[] text) {...}
> ...
> main()
> {
>      match("gr[ae]y", din.read());
> }
> 
> to this:
> 
> RegExpMatch match(char[] pattern)(char[] text) {...}
> ...
> main()
> {
>      match!("gr[ae]y")(din.read());
> }
> 
> Any plans this way?

The above forces only compile-time constant regexps. What we need is to dispatch depending on compile-time constant vs. run-time string.

Combined with macros, this will essentially allow a call to writefln to morph, depending on its arguments, into putch(), fputs(), write(), fprintf() - or execute today's dynamically-checked writefln.


Andrei
March 17, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Reiner Pope wrote:
>> Andrei Alexandrescu (See Website For Email) Wrote:
>>
>>> We have talked about a design. In short, the intent is to define three flavors of immutability:
>>>
>>> a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se;
>>>
>>> b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively);
>>>
>>> c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable.
>>>
>> Is there anything in your discussion about compile-time constants (the sort that you need for 'static if', etc) ? You spoke a while ago about improving compile-time evaluation syntax so that there would be no difference at the call  site between runtime and compile-time evaluation (like for regexps).
> 
> I understand this is high on Walter's list.
Good to hear that.

 The syntax discussed is:
> 
> // dispatched when the pattern is known at compile time
> RegExpMatch match(const char[] p)(const char[] p, char[] text) {...}
> // dispatched when the pattern is a runtime string
> RegExpMatch match()(const char[] p, char[] text) {...}
That seems fine, but I can't help wondering, "why the double-declaration of p in the first instance?" and also, "why does the second instance of match even need to be a template?".

> 
>> It seems like you could get this quite easily by adding just one more parameter modifier (is this a storage class? I'm not sure) to denote, "any time this function is called, this parameter must be known at compile-time". Effectively, it would just be a conversion from this:
>>
>> RegExpMatch match(static const char[] pattern, char[] text) {...}
>> ...
>> main()
>> {
>>      match("gr[ae]y", din.read());
>> }
>>
>> to this:
>>
>> RegExpMatch match(char[] pattern)(char[] text) {...}
>> ...
>> main()
>> {
>>      match!("gr[ae]y")(din.read());
>> }
>>
>> Any plans this way?
> 
> The above forces only compile-time constant regexps. What we need is to dispatch depending on compile-time constant vs. run-time string.
Well, I kind of took it for granted that the above suggestion would allow for overloading by compile-time-constness (with a preference to the compile-time version when there's a choice). So that you could write:

RegExpMatch match(static const char[] pattern, char[] text){...}
RegExpMatch match(char[] pattern, char[] text) {...}

> 
> Combined with macros, this will essentially allow a call to writefln to morph, depending on its arguments, into putch(), fputs(), write(), fprintf() - or execute today's dynamically-checked writefln.

True. I was also interested in another use of this, though: allowing a library-time implementation of tuples (since what kind of feature is one that can't be implemented in the library?). There are basically two reasons that this can't be done at the moment:

alias Tuple!(int, double) myTuple;
myTuple[0] = 5; // First reason
foreach (val; myTuple) // Second reason
{
    writef(val);
}

I would imagine that you could implement the first feature as:

struct MyTupleImpl(TypeList...)
{
   ...
   TypeList[i] opIndex(static const int i) {...}
// We grab the return type based on the parameter, so we must know it at compile time. It makes no sense to have a runtime version of this.
}

and at a stretch, you could imagine the second feature as working in a similar way: your compile-time representation of the foreach delegate is either a char[] to be mixed in, or an expression alias, when we get that:

struct MyTupleImpl(TypeList...)
{
    int opApply(static const char[] code)
    {
        // iterate through tuple; this iteration would actually have to be generated with CTFE, but you get the idea
        {
            TypeList[i] __special_foreach_key = whatever;
            mixin(code);
        }
    }
}

Cheers,

Reiner
March 17, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Combined with macros, this will essentially allow a call to writefln to morph, depending on its arguments, into putch(), fputs(), write(), fprintf() - or execute today's dynamically-checked writefln.
So is there going to be a way to specify at the function (or template) definition site that the function (or template) produces a compile-time string which is to be mixed into the call site. eg:

// The 'mixin' specifies that the result should be mixed into the call site.
mixin char[] writefln(/*params*/)
{
    ... // Generate code
    return generatedCode;
}


Sounds good.


Reiner
March 17, 2007
Reiner Pope wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>> Reiner Pope wrote:
>>> Andrei Alexandrescu (See Website For Email) Wrote:
>>>
>>>> We have talked about a design. In short, the intent is to define three flavors of immutability:
>>>>
>>>> a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se;
>>>>
>>>> b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively);
>>>>
>>>> c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable.
>>>>
>>> Is there anything in your discussion about compile-time constants (the sort that you need for 'static if', etc) ? You spoke a while ago about improving compile-time evaluation syntax so that there would be no difference at the call  site between runtime and compile-time evaluation (like for regexps).
>>
>> I understand this is high on Walter's list.
> Good to hear that.
> 
>  The syntax discussed is:
>>
>> // dispatched when the pattern is known at compile time
>> RegExpMatch match(const char[] p)(const char[] p, char[] text) {...}
>> // dispatched when the pattern is a runtime string
>> RegExpMatch match()(const char[] p, char[] text) {...}
> That seems fine, but I can't help wondering, "why the double-declaration of p in the first instance?" and also, "why does the second instance of match even need to be a template?".

Good question. It must be a template by the pigeonhole principle: each compile-time regex generates in theory different code, so you must have as many distinct functions as strings.

>>> It seems like you could get this quite easily by adding just one more parameter modifier (is this a storage class? I'm not sure) to denote, "any time this function is called, this parameter must be known at compile-time". Effectively, it would just be a conversion from this:
>>>
>>> RegExpMatch match(static const char[] pattern, char[] text) {...}
>>> ...
>>> main()
>>> {
>>>      match("gr[ae]y", din.read());
>>> }
>>>
>>> to this:
>>>
>>> RegExpMatch match(char[] pattern)(char[] text) {...}
>>> ...
>>> main()
>>> {
>>>      match!("gr[ae]y")(din.read());
>>> }
>>>
>>> Any plans this way?
>>
>> The above forces only compile-time constant regexps. What we need is to dispatch depending on compile-time constant vs. run-time string.
> Well, I kind of took it for granted that the above suggestion would allow for overloading by compile-time-constness (with a preference to the compile-time version when there's a choice). So that you could write:
> 
> RegExpMatch match(static const char[] pattern, char[] text){...}
> RegExpMatch match(char[] pattern, char[] text) {...}
> 
>>
>> Combined with macros, this will essentially allow a call to writefln to morph, depending on its arguments, into putch(), fputs(), write(), fprintf() - or execute today's dynamically-checked writefln.
> 
> True. I was also interested in another use of this, though: allowing a library-time implementation of tuples (since what kind of feature is one that can't be implemented in the library?). There are basically two reasons that this can't be done at the moment:
> 
> alias Tuple!(int, double) myTuple;
> myTuple[0] = 5; // First reason
> foreach (val; myTuple) // Second reason
> {
>     writef(val);
> }
> 
> I would imagine that you could implement the first feature as:
> 
> struct MyTupleImpl(TypeList...)
> {
>    ...
>    TypeList[i] opIndex(static const int i) {...}
> // We grab the return type based on the parameter, so we must know it at compile time. It makes no sense to have a runtime version of this.
> }
> 
> and at a stretch, you could imagine the second feature as working in a similar way: your compile-time representation of the foreach delegate is either a char[] to be mixed in, or an expression alias, when we get that:
> 
> struct MyTupleImpl(TypeList...)
> {
>     int opApply(static const char[] code)
>     {
>         // iterate through tuple; this iteration would actually have to be generated with CTFE, but you get the idea
>         {
>             TypeList[i] __special_foreach_key = whatever;
>             mixin(code);
>         }
>     }
> }

That's not on the list yet. One thing at a time :o).


Andrei
March 17, 2007
Andrei Alexandrescu (See Website For Email) wrote:
> Reiner Pope wrote:
>> Andrei Alexandrescu (See Website For Email) wrote:
>>> Reiner Pope wrote:
>>>> Andrei Alexandrescu (See Website For Email) Wrote:
>>>>
>>>>> We have talked about a design. In short, the intent is to define three flavors of immutability:
>>>>>
>>>>> a) final - a simple storage class controlling the immutability of the bits allocated for the symbol per se;
>>>>>
>>>>> b) const - type qualifier meaning an immutable view of an otherwise modifiable data. const does not control the bits of the object, only the storage addressed indirectly by it (transitively);
>>>>>
>>>>> c) "superconst" - denoted as "const!" or "super const": type qualifier meaning that the data is genuinely unmodifiable.
>>>>>
>>>> Is there anything in your discussion about compile-time constants (the sort that you need for 'static if', etc) ? You spoke a while ago about improving compile-time evaluation syntax so that there would be no difference at the call  site between runtime and compile-time evaluation (like for regexps).
>>>
>>> I understand this is high on Walter's list.
>> Good to hear that.
>>
>>  The syntax discussed is:
>>>
>>> // dispatched when the pattern is known at compile time
>>> RegExpMatch match(const char[] p)(const char[] p, char[] text) {...}
>>> // dispatched when the pattern is a runtime string
>>> RegExpMatch match()(const char[] p, char[] text) {...}
>> That seems fine, but I can't help wondering, "why the double-declaration of p in the first instance?" and also, "why does the second instance of match even need to be a template?".
> 
> Good question. It must be a template by the pigeonhole principle: each compile-time regex generates in theory different code, so you must have as many distinct functions as strings.
> 

But how about the runtime implementation of match()? There's no code generation there...


> 
> That's not on the list yet. One thing at a time :o).
Sure thing. It's just that we've already got so much more after D 1.0 that I can't help wanting more :o).

Cheers,

Reiner