Re: higher-order funcs for ranges (with usual interface)

2011-02-02 Thread Lars T. Kyllingstad
On Wed, 02 Feb 2011 13:26:39 +0100, spir wrote:

> Hello,
> 
> This bit of code for arrays:
> 
> Out[] map (In,Out) (In[] input, Out delegate (In) f) {
>  Out[] output = new Out[](input.length); foreach (i,item ; input)
>  output [i] = f(item);
>  return output;
> }
> unittest {
>  char character (uint code) {return cast(char)code;} uint[] codes =
>  [0x61,0x62,0x63];
>  // functional style
>  writeln(map(codes, &character));// "abc" // OO style
>  writeln(codes.map(&character)); // "abc"
> }
> 
> How to write this for ranges? [...]
>
> For ranges, I'm looking for something similar to:
>  Range!Out map (In,Out) (Range!In input, Out delegate (In) f) {...}
> Indeed, the compiler should understand that Range!T is a type id just
> like T[].

I don't think it's possible to do it exactly as you describe.  I mean, 
Range in that case can be anything, and you can't always return a range 
of the same kind.  Two possibilities are, you can do it eagerly,

  Out[] map(Range, In, Out)(Range input, Out delegate(In) f)
  if (isInputRange!Range && is(ElementType!Range : In))
  {
  ...
  }

or you can do it lazily by defining your own map range (untested):

  struct Map(Range, In, Out)
  if (isInputRange!Range && is(ElementType!Range : In)
  {
  Range input;
  Out delegate(In) f;

  @property bool empty() { return input.empty; }

  // Inefficient, should cache front...
  @property Out front() { return f(input.front); }
  
  void popFront() { input.popFront(); }
  }

  Map!(Range, Out) map(Range, In, Out)(Range input, Out delegate(In) f)
  if (isInputRange!R && is(ElementType!Range : In)
  {
  return typeof(return)(input, f);
  }

-Lars


Re: higher-order funcs for ranges (with usual interface)

2011-02-02 Thread Lars T. Kyllingstad
On Wed, 02 Feb 2011 13:18:07 +, Lars T. Kyllingstad wrote:

> [...]
> 
>   struct Map(Range, In, Out)
>   if (isInputRange!Range && is(ElementType!Range : In)
>   {
>   Range input;
>   Out delegate(In) f;
> 
>   @property bool empty() { return input.empty; }
> 
>   // Inefficient, should cache front... @property Out front() {
>   return f(input.front); }
>   
>   void popFront() { input.popFront(); }
>   }
> 
>   Map!(Range, Out) map(Range, In, Out)(Range input, Out delegate(In) f)
>   if (isInputRange!R && is(ElementType!Range : In)
>   {
>   return typeof(return)(input, f);
>   }

Oops, seems i missed a few closing parentheses on the template 
constraints.

-Lars


Re: higher-order funcs for ranges (with usual interface)

2011-02-02 Thread spir

On 02/02/2011 02:18 PM, Lars T. Kyllingstad wrote:

On Wed, 02 Feb 2011 13:26:39 +0100, spir wrote:


Hello,

This bit of code for arrays:

Out[] map (In,Out) (In[] input, Out delegate (In) f) {
  Out[] output = new Out[](input.length); foreach (i,item ; input)
  output [i] = f(item);
  return output;
}
unittest {
  char character (uint code) {return cast(char)code;} uint[] codes =
  [0x61,0x62,0x63];
  // functional style
  writeln(map(codes,&character));// "abc" // OO style
  writeln(codes.map(&character)); // "abc"
}

How to write this for ranges? [...]

For ranges, I'm looking for something similar to:
  Range!Out map (In,Out) (Range!In input, Out delegate (In) f) {...}
Indeed, the compiler should understand that Range!T is a type id just
like T[].


I don't think it's possible to do it exactly as you describe.  I mean,
Range in that case can be anything, and you can't always return a range
of the same kind.


Right. The output range's ElementType is given by f's return type. As you say, 
the "kind" of range may change. Even if it's the same, how could one express 
that: , syntactically and in the param set, 
just like  is written "T[]"?
Currently, we must (1) declare the range type as template param, which is a bit 
redondant because the ElementType must also be given, (2) add some 'is' horror 
code:

 if (isInputRange!Range && is(ElementType!Range : In))
I guess the only solution would be for the compiler to support a kind of reange 
type syntax?



  Two possibilities are, you can do it eagerly,

   Out[] map(Range, In, Out)(Range input, Out delegate(In) f)
   if (isInputRange!Range&&  is(ElementType!Range : In))
   {
   ...
   }


OK.


or you can do it lazily by defining your own map range (untested):

   struct Map(Range, In, Out)
   if (isInputRange!Range&&  is(ElementType!Range : In)
   {
   Range input;
   Out delegate(In) f;

   @property bool empty() { return input.empty; }

   // Inefficient, should cache front...
   @property Out front() { return f(input.front); }

   void popFront() { input.popFront(); }
   }


That's similar to what I did.


   Map!(Range, Out) map(Range, In, Out)(Range input, Out delegate(In) f)
   if (isInputRange!R&&  is(ElementType!Range : In)
   {
   return typeof(return)(input, f);
   }


What's the point of map, then? My version initially had a 'MapRange' defined as 
static struct template inside map, but then map just instanciated it, so I 
suppressed map alltogether, letting the user write:

auto r2 = MapRange!(R1, In, Out)(input, f);
which is not more complicated than calling the func, I guess.


-Lars


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



Re: higher-order funcs for ranges (with usual interface)

2011-02-02 Thread Lars T. Kyllingstad
On Wed, 02 Feb 2011 18:38:02 +0100, spir wrote:

> On 02/02/2011 02:18 PM, Lars T. Kyllingstad wrote:
>> On Wed, 02 Feb 2011 13:26:39 +0100, spir wrote:
>>
>>> Hello,
>>>
>>> This bit of code for arrays:
>>>
>>> Out[] map (In,Out) (In[] input, Out delegate (In) f) {
>>>   Out[] output = new Out[](input.length); foreach (i,item ; input)
>>>   output [i] = f(item);
>>>   return output;
>>> }
>>> unittest {
>>>   char character (uint code) {return cast(char)code;} uint[] codes
>>>   = [0x61,0x62,0x63];
>>>   // functional style
>>>   writeln(map(codes,&character));// "abc" // OO style
>>>   writeln(codes.map(&character)); // "abc"
>>> }
>>>
>>> How to write this for ranges? [...]
>>>
>>> For ranges, I'm looking for something similar to:
>>>   Range!Out map (In,Out) (Range!In input, Out delegate (In) f)
>>>   {...}
>>> Indeed, the compiler should understand that Range!T is a type id just
>>> like T[].
>>
>> I don't think it's possible to do it exactly as you describe.  I mean,
>> Range in that case can be anything, and you can't always return a range
>> of the same kind.
> 
> Right. The output range's ElementType is given by f's return type. As
> you say, the "kind" of range may change. Even if it's the same, how
> could one express that: ,
> syntactically and in the param set, just like
>  is written "T[]"? Currently, we must
> (1) declare the range type as template param, which is a bit redondant
> because the ElementType must also be given, (2) add some 'is' horror
> code:
>   if (isInputRange!Range && is(ElementType!Range : In))
> I guess the only solution would be for the compiler to support a kind of
> reange type syntax?

I'm not sure I understand what you mean here.  Perhaps you're looking for 
something like concepts, which have been discussed for both D and C++0x 
but rejected in both languages:

http://en.wikipedia.org/wiki/Concept_%28generic_programming%29


Anyway, if the source and target range are of the same (known) kind, 
something like this should work:

struct MyRange(T) { ... }

MyRange!Out map(In, Out)(MyRange!In input, Out delegate(In) f)
{
...
}

If they are of different kinds, but still known, this should work:

struct MySourceRange(T) { ... }
struct MyTargetRange(T) { ... }

MyTargetRange!Out map(In, Out)
(MySourceRange!In input, Out delegate(In) f)
{
...
}

Note that I am only talking about what the compiler should be able to 
figure out through IFTI (implicit function template instantiation), and 
not about actual implementation.


>>   Two possibilities are, you can do it eagerly,
>>
>>Out[] map(Range, In, Out)(Range input, Out delegate(In) f)
>>if (isInputRange!Range&&  is(ElementType!Range : In))
>>{
>>...
>>}
> 
> OK.
> 
>> or you can do it lazily by defining your own map range (untested):
>>
>>struct Map(Range, In, Out)
>>if (isInputRange!Range&&  is(ElementType!Range : In)
>>{
>>Range input;
>>Out delegate(In) f;
>>
>>@property bool empty() { return input.empty; }
>>
>>// Inefficient, should cache front... @property Out front() {
>>return f(input.front); }
>>
>>void popFront() { input.popFront(); }
>>}
> 
> That's similar to what I did.
> 
>>Map!(Range, Out) map(Range, In, Out)(Range input, Out delegate(In)
>>f)
>>if (isInputRange!R&&  is(ElementType!Range : In)
>>{
>>return typeof(return)(input, f);
>>}
> 
> What's the point of map, then? My version initially had a 'MapRange'
> defined as static struct template inside map, but then map just
> instanciated it, so I suppressed map alltogether, letting the user
> write:
>   auto r2 = MapRange!(R1, In, Out)(input, f);
> which is not more complicated than calling the func, I guess.

map() is just a helper function.   Unlike struct literals/constructors, 
functions can make use of IFTI, which makes for much prettier code:

// The range from my example, without map()
auto result =
Map!(SomeRange!int, int, bool)(someRange, someDelegate);

// The range from my example, with map()
auto result = map(someInputRange, someDelegate);

This has become a quite common idiom in Phobos.  std.range, for instance, 
is littered with helper functions like this:

  Retro, retro()
  Stride, stride()
  Chain, chain()
  ...

The list goes on.

-Lars


Re: higher-order funcs for ranges (with usual interface)

2011-02-03 Thread spir

On 02/03/2011 08:41 AM, Lars T. Kyllingstad wrote:

On Wed, 02 Feb 2011 18:38:02 +0100, spir wrote:



I guess the only solution would be for the compiler to support a kind of
reange type syntax?


I'm not sure I understand what you mean here.  Perhaps you're looking for
something like concepts, which have been discussed for both D and C++0x
but rejected in both languages:

 http://en.wikipedia.org/wiki/Concept_%28generic_programming%29


Yes, I know about concepts ;-) (and typestates, and such). That's not what I 
mean but I could not find how to express it. What I have in mind is a way to 
simply express  just like  is expressed by "T[]". But 
indeed the issue is there is only one type of array of T, while there are an 
infinity of types of ranges of T.

Your solution below is a good alternative.


Anyway, if the source and target range are of the same (known) kind,
something like this should work:

 struct MyRange(T) { ... }

 MyRange!Out map(In, Out)(MyRange!In input, Out delegate(In) f)
 {
 ...
 }

If they are of different kinds, but still known, this should work:

 struct MySourceRange(T) { ... }
 struct MyTargetRange(T) { ... }

 MyTargetRange!Out map(In, Out)
 (MySourceRange!In input, Out delegate(In) f)
 {
 ...
 }

Note that I am only talking about what the compiler should be able to
figure out through IFTI (implicit function template instantiation), and
not about actual implementation.


Right, this is more or less what I was looking for. And I think I can restrict 
cases to ranges beeing of the same "kind". If necessary, the result can then be 
mapped onto another kind of range (hopefully lazily).
The only "un-workaround-able" situation is, I guess, when the source range is 
infinite ;-)



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



Re: higher-order funcs for ranges (with usual interface)

2011-02-03 Thread Lars T. Kyllingstad
On Thu, 03 Feb 2011 13:05:00 +0100, spir wrote:

> On 02/03/2011 08:41 AM, Lars T. Kyllingstad wrote:
>> On Wed, 02 Feb 2011 18:38:02 +0100, spir wrote:
> 
>>> I guess the only solution would be for the compiler to support a kind
>>> of reange type syntax?
>>
>> I'm not sure I understand what you mean here.  Perhaps you're looking
>> for something like concepts, which have been discussed for both D and
>> C++0x but rejected in both languages:
>>
>>  http://en.wikipedia.org/wiki/Concept_%28generic_programming%29
> 
> Yes, I know about concepts ;-) (and typestates, and such). That's not
> what I mean but I could not find how to express it. What I have in mind
> is a way to simply express  just like  is
> expressed by "T[]". But indeed the issue is there is only one type of
> array of T, while there are an infinity of types of ranges of T.
> Your solution below is a good alternative.
> 
>> Anyway, if the source and target range are of the same (known) kind,
>> something like this should work:
>>
>>  struct MyRange(T) { ... }
>>
>>  MyRange!Out map(In, Out)(MyRange!In input, Out delegate(In) f) {
>>  ...
>>  }
>>
>> If they are of different kinds, but still known, this should work:
>>
>>  struct MySourceRange(T) { ... }
>>  struct MyTargetRange(T) { ... }
>>
>>  MyTargetRange!Out map(In, Out)
>>  (MySourceRange!In input, Out delegate(In) f)
>>  {
>>  ...
>>  }
>>
>> Note that I am only talking about what the compiler should be able to
>> figure out through IFTI (implicit function template instantiation), and
>> not about actual implementation.
> 
> Right, this is more or less what I was looking for. And I think I can
> restrict cases to ranges beeing of the same "kind". If necessary, the
> result can then be mapped onto another kind of range (hopefully lazily).
> The only "un-workaround-able" situation is, I guess, when the source
> range is infinite ;-)

Why the reluctance to use template constraints?  They're so flexible! :)

-Lars


Re: higher-order funcs for ranges (with usual interface)

2011-02-03 Thread spir

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.


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



Re: higher-order funcs for ranges (with usual interface)

2011-02-03 Thread Lars T. Kyllingstad
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

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


Re: higher-order funcs for ranges (with usual interface)

2011-02-03 Thread spir

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.)

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



Re: higher-order funcs for ranges (with usual interface)

2011-02-07 Thread Lars T. Kyllingstad
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


Re: higher-order funcs for ranges (with usual interface)

2011-02-07 Thread spir

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



Re: higher-order funcs for ranges (with usual interface)

2011-02-07 Thread Torarin
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


Re: higher-order funcs for ranges (with usual interface)

2011-02-07 Thread spir

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