equivariant functions

Denis Koroskin 2korden at gmail.com
Tue Oct 14 12:30:47 PDT 2008


On Tue, 14 Oct 2008 23:12:43 +0400, Simen Kjaeraas  
<simen.kjaras at gmail.com> wrote:

> On Tue, 14 Oct 2008 20:57:56 +0200, Denis Koroskin <2korden at gmail.com>  
> wrote:
>
>> On Tue, 14 Oct 2008 22:38:58 +0400, Simen Kjaeraas  
>> <simen.kjaras at gmail.com> wrote:
>>
>>> On Tue, 14 Oct 2008 18:51:56 +0200, Andrei Alexandrescu  
>>> <SeeWebsiteForEmail at erdani.org> wrote:
>>>
>>>> Steven Schveighoffer wrote:
>>>>> "Andrei Alexandrescu" wrote
>>>>>> Steven Schveighoffer wrote:
>>>>>>> "Andrei Alexandrescu" wrote
>>>>>>>> I discussed with Walter a variant that implements equivariant  
>>>>>>>> functions without actually adding an explicit feature to the  
>>>>>>>> language. Consider:
>>>>>>>>
>>>>>>>> typeof(s) stripl(const(char)[] s);
>>>>>>> As another point on this, I think someone else mentioned it, but I  
>>>>>>> can't find the post.
>>>>>>>
>>>>>>> I don't like the way this looks.  The way it reads is 'stripl  
>>>>>>> returns the same type as s', but really, the typeof(s) is actually  
>>>>>>> modifying the type of the argument also.  This seems very  
>>>>>>> unintuitive.
>>>>>> I agree. We need to look for a better notation.
>>>>>>
>>>>>>> I understand the need to not change the language, but I think most  
>>>>>>> would prefer a syntax where the type modifier is specified on at  
>>>>>>> least the argument.  People are going to be extremely confused  
>>>>>>> when they can't treat 's' like a normal const(char)[].
>>>>>>>
>>>>>>> If the ultimate result is that no intuitive syntax can be made  
>>>>>>> without changing the language, then I think it is more important  
>>>>>>> to have this feature than to not change the language.
>>>>>>>
>>>>>>> One other syntax that Janice proposed (and I later put into a  
>>>>>>> bugzilla), is to use the dead keyword inout.  Meaning, what you  
>>>>>>> send in is what you get out.  ref already completely replaces  
>>>>>>> inout, so there is no need to keep it under its current meaning:
>>>>>>>
>>>>>>> inout(char)[] stripl(inout(char)[] s);
>>>>>>>
>>>>>>> I'm not in love with this completely, but it has the benefit of  
>>>>>>> not requiring a new keyword.
>>>>>> Also I guess:
>>>>>>
>>>>>> class C
>>>>>> {
>>>>>>     Range!(inout(C)) foo() inout;
>>>>>> }
>>>>>>
>>>>>> And also:
>>>>>>
>>>>>> class Base {}
>>>>>> class Derived : Base {}
>>>>>> inout foo(inout Base b);
>>>>>  In this last example, what does the inout mean at the front?
>>>>
>>>> Accept and return any subtype of Base.
>>>>
>>>> Andrei
>>>
>>> I was first wondering how this would treat things like
>>>
>>>    inout foo(Base b, int n);
>>>
>>> But then I noticed it saying "inout Base b", so only the argument(s)  
>>> marked
>>> inout are considered for the return type?
>>
>> Yes
>>
>>> Also, this means a maximum of one
>>> inout argument type per function, right?
>>>
>>
>> No, there were many example of multiple (and even none) inout function  
>> arguments.
>
> I said argument types, as inout(MyClass) in inout foo(inout(MyClass) a,  
> inout(MyClass) b).
>

Isn't the following example good enough?
inout(A) min(inout(A1) a1, inout(A2) a2); // 3 different types

How about another one:

class Foo
{
     inout this(inout(char)[] chars, inout(int)[] ints)
     {
         this.chars = chars;
         this.ints = ints;
     }

     char[] chars;
     int[] ints;
}

inout(Foo) createFoo(inout(char)[] chars, inout(int)[] ints)
{
     return new Foo(chars, ints);
}

auto foo = createFoo("Hello!", [1, 1, 2, 3, 5, 8]); // typeof(foo) ==  
invariant(Foo)

The basic idea is to get some arrays of different types and return some  
other (corresponding) object. Their types may be completely unrelated!

Also this one:

class Foo
{
     inout(Bar) getBar() inout(this) { return bar; } // takes 'this' of  
type inout(Foo) an returns inout(Bar). Types are unrelated.
     private Bar bar;
}

>>> As for the keyword, I feel inout is better than typeof, and good  
>>> enough. Not perfect, but good enough.
>>>
>>
>> My concern is that it than 'inout' doesn't express that all the  
>> arguments marked as inout as bound to each other and actual type is  
>> deduced from them all. There is a relationship between their types.
>>
>> inout(A) min(inout(A1) a1, inout(A2) a2);
>>
>> A1 a1;
>> A2 a2;
>>
>> const(A1) ca1;
>> const(A2) ca2;
>>
>> invariant(A1) ia1;
>> invariant(A1) ia2;
>>
>> auto a = min(a1, ia2); // typeof(a) == const(A) even though neither a1  
>> nor ia2 of that type
>> auto a = min(a1, a2);  // typeof(a) == A
>>
>> You see, in the example above the return type is changed because type  
>> of 2nd argument is changed. Not only the return type but types of all  
>> the arguments, too.
>>
>> auto a = min(ia1, ia2);  // typeof(a) == invariant(A). Now return type  
>> is changed per 1st argument type change.
>
> I don't really see this as a problem. Returning mutable or invariant  
> would be worse. Anyways, if you need the two arguments to be of the same  
> type, I'd prefer this syntax:
>
>    inout min(inout(A) a1, typeof(a1) a2){}
>

Ouch! This one hurts. Yet it is inconsistent with multiple argument types.
Besides, what advantages of it do you see over "inout min(inout(A) a1,  
inout(A) a2);"?



More information about the Digitalmars-d mailing list