higher-order funcs for ranges (with usual interface)

spir denis.spir at gmail.com
Mon Feb 7 03:04:06 PST 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



More information about the Digitalmars-d-learn mailing list