February 07, 2011
On Thu, 03 Feb 2011 19:11:04 +0100, spir wrote:

> On 02/03/2011 02:25 PM, Lars T. Kyllingstad wrote:
>> On Thu, 03 Feb 2011 13:53:44 +0100, spir wrote:
>>
>>> On 02/03/2011 01:17 PM, Lars T. Kyllingstad wrote:
>>>> Why the reluctance to use template constraints?  They're so flexible! :)
>>>
>>> I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily
>>> get rid of it in favor of type-classes (built eg as an extension to
>>> current interfaces). For instance, instead of:
>>>
>>>       void func (T) (T t)
>>>           if (is(someConstraint1)&&  is(someConstraint2))
>>>       {
>>>           ...
>>>       }
>>>
>>> use:
>>>
>>>       void func (SomeTypeClass T) (T t)
>>>       {
>>>           ...
>>>       }
>>>
>>> For instance (untested):
>>>
>>>       void func (T) (T t)
>>>           if (isInputRange(T)&&  is(ElementType!T == E))
>>> -->
>>>       void func (InputRange!E T) (T t)
>>>
>>> where InputRange is a (templated) interface / type-class.
>>>
>>> Type-class checks on /type/ /template/ parameters (as opposed to type
>>> checks on regular value parameters) would be performed structurally
>>> (as opposed to nominally). D knows how to do this, since that's what
>>> it needs to perform when checking is() constraints.
>>
>> I agree that is() is rather ugly.  Same with __traits.  If you haven't already done so, I suggest you vote up this issue:
>>
>>    http://d.puremagic.com/issues/show_bug.cgi?id=3702
> 
> Done!
> (I did not get all the details 'cause no time for a deep look, but
> anything impulsed by the motivation of getting rid of is() and __traits
> can hardly be a Bad Thing ;-)
> 
> What do you think of type classes, as an alternative to Don's proposal
> in issue #3702.
> See also "Type Classes as Objects and Implicits":
> http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf
> 
>> Anyway, you can hide is()'s ugliness in the most common cases, though, by defining new templates.  For instance, I wouldn't mind having the following in std.range as an overload of isInputRange:
>>
>>    template isInputRange(R, T)
>>    {
>>        enum isInputRange = isInputRange!R&&  is(ElementType!R == T);
>>    }
>>
>> Then, you'd simply write
>>
>>    void func(R)(R range) if (isInputRange!(R, E)) { ... }
>>
>> -Lars
> 
> A great improvement, indeed.
> 
> While we're at defining a set of constraints in a template, let us make
> it an interface / type-class that the E must (structurally) satisfy, and
> just write:
>      void func(InputRange!E R)(R range) { ... }
> 
> What do you think?
> 
> Note: a template is not always required, I guess:
>      void writeElements (Iterable Elements) (Elements elements) {
> 	foreach (element, elements) {
>              write(element,' ');
>          }
>      }
> (In this case, because write is itself generic.)


How would you deal with the case where the input must satisfy more than one concept/constraint?  I mean, for the simple case where you say "R must be an input range of E", sure, type classes/concepts are cleaner. But what about the case where, say, you want R to be an infinite random access range that supports slicing?  With template constraints it's simple:

    void doStuff(R)(R someRange)
        if (isRandomAccessRange!R && isInfinite!R && hasSlicing!R)
    {
        ...
    }

Now, I'm no expert on concepts at all---my main sources of information about them are superficial comments on the D newsgroup and a quick browse of the Wikipedia page---but it seems to me that you'd have to define a new concept for each such combination of constraints.  Or?

-Lars
February 07, 2011
On 02/07/2011 09:18 AM, Lars T. Kyllingstad wrote:
>>>> I cannot stand the "is()" idiom/syntax ;-) Dunno why. Would happily
>>>> >>>  get rid of it in favor of type-classes (built eg as an extension to
>>>> >>>  current interfaces). For instance, instead of:
>>>> >>>
>>>> >>>         void func (T) (T t)
>>>> >>>             if (is(someConstraint1)&&   is(someConstraint2))
>>>> >>>         {
>>>> >>>             ...
>>>> >>>         }
>>>> >>>
>>>> >>>  use:
>>>> >>>
>>>> >>>         void func (SomeTypeClass T) (T t)
>>>> >>>         {
>>>> >>>             ...
>>>> >>>         }
>>>> >>>
>>>> >>>  For instance (untested):
>>>> >>>
>>>> >>>         void func (T) (T t)
>>>> >>>             if (isInputRange(T)&&   is(ElementType!T == E))
>>>> >>>  -->
>>>> >>>         void func (InputRange!E T) (T t)
>>>> >>>
>>>> >>>  where InputRange is a (templated) interface / type-class.
>>>> >>>
>>>> >>>  Type-class checks on/type/  /template/  parameters (as opposed to type
>>>> >>>  checks on regular value parameters) would be performed structurally
>>>> >>>  (as opposed to nominally). D knows how to do this, since that's what
>>>> >>>  it needs to perform when checking is() constraints.
>>> >>
>>> >>  I agree that is() is rather ugly.  Same with __traits.  If you haven't
>>> >>  already done so, I suggest you vote up this issue:
>>> >>
>>> >>      http://d.puremagic.com/issues/show_bug.cgi?id=3702
>> >
>> >  Done!
>> >  (I did not get all the details 'cause no time for a deep look, but
>> >  anything impulsed by the motivation of getting rid of is() and __traits
>> >  can hardly be a Bad Thing ;-)
>> >
>> >  What do you think of type classes, as an alternative to Don's proposal
>> >  in issue #3702.
>> >  See also "Type Classes as Objects and Implicits":
>> >  http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf
>> >
>>> >>  Anyway, you can hide is()'s ugliness in the most common cases, though,
>>> >>  by defining new templates.  For instance, I wouldn't mind having the
>>> >>  following in std.range as an overload of isInputRange:
>>> >>
>>> >>      template isInputRange(R, T)
>>> >>      {
>>> >>          enum isInputRange = isInputRange!R&&   is(ElementType!R == T);
>>> >>      }
>>> >>
>>> >>  Then, you'd simply write
>>> >>
>>> >>      void func(R)(R range) if (isInputRange!(R, E)) { ... }
>>> >>
>>> >>  -Lars
>> >
>> >  A great improvement, indeed.
>> >
>> >  While we're at defining a set of constraints in a template, let us make
>> >  it an interface / type-class that the E must (structurally) satisfy, and
>> >  just write:
>> >        void func(InputRange!E R)(R range) { ... }
>> >
>> >  What do you think?
>> >
>> >  Note: a template is not always required, I guess:
>> >        void writeElements (Iterable Elements) (Elements elements) {
>> >  	foreach (element, elements) {
>> >                write(element,' ');
>> >            }
>> >        }
>> >  (In this case, because write is itself generic.)
>
> How would you deal with the case where the input must satisfy more than
> one concept/constraint?  I mean, for the simple case where you say "R
> must be an input range of E", sure, type classes/concepts are cleaner.
> But what about the case where, say, you want R to be an infinite random
> access range that supports slicing?  With template constraints it's
> simple:
>
>      void doStuff(R)(R someRange)
>          if (isRandomAccessRange!R&&  isInfinite!R&&  hasSlicing!R)
>      {
>          ...
>      }
>
> Now, I'm no expert on concepts at all---my main sources of information
> about them are superficial comments on the D newsgroup and a quick browse
> of the Wikipedia page---but it seems to me that you'd have to define a
> new concept for each such combination of constraints.  Or?

Well, dunno really. If a language implements type classes, let us see how things work there :-)

Note that above, you use 3 implicite type-class defining check funcs. Agreed? Certainly, because they are common, one wrote a fucn to wrap a bigger set of is() stuff. Replacing them with a type-class interface allows
(1) reusing an interface for what it's meant: defining a (super) type, instead of a func which is appropriate and does /not/ correctly convey the meaning
(2) avoiding is()

Now, you may be right in that type-class check may need be written externally:

void doStuff(R)(R someRange)
          if (RandomAccessRange R && InfiniteRange R &&  Slicable R)

or not:

interface ASpecialOne : RandomAccessRange, InfiniteRange, Slicable {}
void doStuff (ASpecialOneR) (R someRange)

Ain't that clean? For sure, if I was to define a new static PL, I would go /that/ way for generic constraints.
This remembers me about another option maybe: when I have time, I'll go and see how XL does it. If (you do not know XL, then /really/ have a look when you have time: http://en.wikipedia.org/wiki/XL_%28programming_language%29 ;esp explore the notion of "conceptual programming")

Denis
-- 
_________________
vita es estrany
spir.wikidot.com

February 07, 2011
If you want to use an interface as a concept, you can take kenji's adaptTo module and add this:

template conformsTo(T, Interfaces...)
{
 enum conformsTo = AdaptTo!Interfaces.hasRequiredMethods!T;
}

and use it like this

void draw(T)(T shape) if (conformsTo!(T, Shape, Drawable))

This will of course only work for methods, not properties or aliases, so you still need constraints in some cases.

Torarin
February 07, 2011
On 02/07/2011 01:07 PM, Torarin wrote:
> If you want to use an interface as a concept, you can take kenji's
> adaptTo module and add this:
>
> template conformsTo(T, Interfaces...)
> {
>   enum conformsTo = AdaptTo!Interfaces.hasRequiredMethods!T;
> }
>
> and use it like this
>
> void draw(T)(T shape) if (conformsTo!(T, Shape, Drawable))
>
> This will of course only work for methods, not properties or aliases,
> so you still need constraints in some cases.

That's nice, thank you.

Denis
-- 
_________________
vita es estrany
spir.wikidot.com

1 2
Next ›   Last »