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