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