higher-order funcs for ranges (with usual interface)

Lars T. Kyllingstad public at kyllingen.NOSPAMnet
Mon Feb 7 00:18:38 PST 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


More information about the Digitalmars-d-learn mailing list