www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A Briefer Syntax for Using Concepts

reply "w0rp" <devw0rp gmail.com> writes:
Here is a question, is it possible for D, or any future language, 
to eventually take something like this...

void foo(InputRange)(InputRange range) 
if(isInputRange!InputRange);

...and to instead be able to write it like this?

void foo(InputRange range);

Where the latter expands into something like the former, and 
InputRange is not a type. How to declare such a thing in the 
first place doesn't matter that much. There are many ways that 
could be done. I'm just wondering if the above is possible at all.
May 07 2014
next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 7 May 2014 at 11:57:51 UTC, w0rp wrote:
 Here is a question, is it possible for D, or any future 
 language, to eventually take something like this...

 void foo(InputRange)(InputRange range) 
 if(isInputRange!InputRange);

 ...and to instead be able to write it like this?

 void foo(InputRange range);

 Where the latter expands into something like the former, and 
 InputRange is not a type. How to declare such a thing in the 
 first place doesn't matter that much. There are many ways that 
 could be done. I'm just wondering if the above is possible at 
 all.

I sense a polymorphic compile-time-only type system.
May 07 2014
prev sibling next sibling parent "Sergei Nosov" <sergei.nosov gmail.com> writes:
On Wednesday, 7 May 2014 at 11:57:51 UTC, w0rp wrote:
 Here is a question, is it possible for D, or any future 
 language, to eventually take something like this...

 void foo(InputRange)(InputRange range) 
 if(isInputRange!InputRange);

 ...and to instead be able to write it like this?

 void foo(InputRange range);

 Where the latter expands into something like the former, and 
 InputRange is not a type. How to declare such a thing in the 
 first place doesn't matter that much. There are many ways that 
 could be done. I'm just wondering if the above is possible at 
 all.

C++ concepts has similar syntax. http://isocpp.org/blog/2013/02/concepts-lite-constraining-templates-with-predicates-andrew-sutton-bjarne-s template<Sortable Cont> void sort(Cont& container); I don't think making InputRange behave as you suggest in void foo(InputRange range); is a valid options, since - how do you handle the situation when you want to accept 2 InputRanges of possibly distinct types? void foo(InputRange range1, InputRange range2); // how to specify that InputRange should be exactly the same type? or possibly distinct types?
May 07 2014
prev sibling next sibling parent "Mason McGill" <mmcgill caltech.edu> writes:
On Wednesday, 7 May 2014 at 11:57:51 UTC, w0rp wrote:
 Here is a question, is it possible for D, or any future 
 language, to eventually take something like this...

 void foo(InputRange)(InputRange range) 
 if(isInputRange!InputRange);

 ...and to instead be able to write it like this?

 void foo(InputRange range);

 Where the latter expands into something like the former, and 
 InputRange is not a type. How to declare such a thing in the 
 first place doesn't matter that much. There are many ways that 
 could be done. I'm just wondering if the above is possible at 
 all.

I think Julia does something like this, under the hood. Julia functions are instantiated once for every concrete argument type tuple they are invoked with: f(x) = x + 2 f(5) # Instantiation for Int64 f(5.0) # Instantiation for Float64 The assembly for these instantiations can be inspected by calling `code_native`. This works well for Julia because its runtime and type system are designed around being able to do this (JIT compilation, multiple dispatch, duck typing, immutable types). Programmers try to use run-time and compile-time polymorphism for the same thing: expressing operations on abstract types. But, each approach has its own advantage (CT: speed; RT: dynamic dispatch). I think the key insight is that the optimal implementation (RT vs. CT) depends only on how the generic function is used, not how it is defined. So, the choice of implementation can, in theory, be left up to a sufficiently-sophisticated runtime/JIT compiler. This seems to be what Julia has done (possibly along with some other Lispy languages; I'm not sure). However, this scheme can't entirely replace D-style templates (e.g. pattern matching). So, Julia still lets you define templates (which they call "objects with static parameters"). It would be great to have something like this in D (with compile-time semantics, of course).
May 07 2014
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Sergei Nosov:

 void foo(InputRange range1, InputRange range2); // how to 
 specify that InputRange should be exactly the same type? or 
 possibly distinct types?

I think the Concepts lite proposal faces this problem too. Take a look. But so far Andrei was against the idea of having lite concepts in D (I am not sure why). Bye, bearophile
May 07 2014
prev sibling next sibling parent "w0rp" <devw0rp gmail.com> writes:
On Wednesday, 7 May 2014 at 12:47:29 UTC, Sergei Nosov wrote:
 void foo(InputRange range1, InputRange range2); // how to 
 specify that InputRange should be exactly the same type? or 
 possibly distinct types?

One thing to consider would be that InputRange wouldn't be a type itself, but range1 and range2 would have types. I suppose you do do things like if(typeof(range1) == typeof(range2)) and so on.
May 07 2014
prev sibling next sibling parent Orvid King via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 5/7/14, w0rp via Digitalmars-d <digitalmars-d puremagic.com> wrote:
 void foo(InputRange range);

How to make it accept multiple types? Simple, we already have template constraints, so this would be how to do it, where T is the element type of the input range: void foo(T)(InputRange!T range);
May 07 2014
prev sibling next sibling parent =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= <per.nordlow gmail.com> writes:
 void foo(T)(InputRange!T range);

Clever. Shouldn't we all, including Phobos, use this shorter syntax instead? Further, we could always do alias R = InputRange!T; or alias R = typeof(range); inside the body if needed.
May 07 2014
prev sibling next sibling parent =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= <per.nordlow gmail.com> writes:
 void foo(T)(InputRange!T range);

Update: Ahh, it seems this syntax is currently accepted by DMD. Should it be? Original function: import std.range: isInputRange; bool allEqual(R)(R range) safe pure nothrow if (isInputRange!R) { import std.algorithm: findAdjacent; import std.range: empty; return range.findAdjacent!("a != b").empty; } unittest { assert([11, 11].allEqual); } unittest { assert(![11, 12].allEqual); } unittest { int[] x; assert(x.allEqual); } compiles. New function: import std.range: InputRange; bool allEqual_(T)(InputRange!T range) safe pure nothrow { import std.algorithm: findAdjacent; import std.range: empty; return range.findAdjacent!("a != b").empty; } unittest { assert([11, 11].allEqual_); } unittest { assert(![11, 12].allEqual_); } unittest { int[] x; assert(x.allEqual_); } fails as algorithm_ex.d(190,27): Error: template algorithm_ex.allEqual_ cannot deduce function from argument types !()(int[]), candidates are: algorithm_ex.d(184,6): algorithm_ex.allEqual_(T)(InputRange!T range) algorithm_ex.d(191,28): Error: template algorithm_ex.allEqual_ cannot deduce function from argument types !()(int[]), candidates are: algorithm_ex.d(184,6): algorithm_ex.allEqual_(T)(InputRange!T range) algorithm_ex.d(192,29): Error: template algorithm_ex.allEqual_ cannot deduce function from argument types !()(int[]), candidates are: algorithm_ex.d(184,6): algorithm_ex.allEqual_(T)(InputRange!T range)
May 07 2014
prev sibling next sibling parent =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= <per.nordlow gmail.com> writes:
 Ahh, it seems this syntax is currently accepted by DMD. Should 
 it be?

Correction: Ahh, it seems this syntax is currently *not* accepted by DMD. Should it be?
May 07 2014
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 7 May 2014 at 16:04:39 UTC, Nordlöw wrote:
 void foo(T)(InputRange!T range);

Update: Ahh, it seems this syntax is currently accepted by DMD. Should it be? Original function: import std.range: isInputRange; bool allEqual(R)(R range) safe pure nothrow if (isInputRange!R) { import std.algorithm: findAdjacent; import std.range: empty; return range.findAdjacent!("a != b").empty; } unittest { assert([11, 11].allEqual); } unittest { assert(![11, 12].allEqual); } unittest { int[] x; assert(x.allEqual); } compiles. New function: import std.range: InputRange; bool allEqual_(T)(InputRange!T range) safe pure nothrow { import std.algorithm: findAdjacent; import std.range: empty; return range.findAdjacent!("a != b").empty; } unittest { assert([11, 11].allEqual_); } unittest { assert(![11, 12].allEqual_); } unittest { int[] x; assert(x.allEqual_); } fails as algorithm_ex.d(190,27): Error: template algorithm_ex.allEqual_ cannot deduce function from argument types !()(int[]), candidates are: algorithm_ex.d(184,6): algorithm_ex.allEqual_(T)(InputRange!T range) algorithm_ex.d(191,28): Error: template algorithm_ex.allEqual_ cannot deduce function from argument types !()(int[]), candidates are: algorithm_ex.d(184,6): algorithm_ex.allEqual_(T)(InputRange!T range) algorithm_ex.d(192,29): Error: template algorithm_ex.allEqual_ cannot deduce function from argument types !()(int[]), candidates are: algorithm_ex.d(184,6): algorithm_ex.allEqual_(T)(InputRange!T range)

std.range.InputRange is an interface template. int[] is not implicitly convertable to InputRange!(T[]), which is in fact an interface to a range with element type int[] (not int) std.range.isInputRange and std.range.InputRange are unrelated except that the latter satisfies the former.
May 07 2014
prev sibling next sibling parent "w0rp" <devw0rp gmail.com> writes:
On Wednesday, 7 May 2014 at 14:49:17 UTC, Orvid King via 
Digitalmars-d wrote:
 On 5/7/14, w0rp via Digitalmars-d <digitalmars-d puremagic.com> 
 wrote:
 void foo(InputRange range);

How to make it accept multiple types? Simple, we already have template constraints, so this would be how to do it, where T is the element type of the input range: void foo(T)(InputRange!T range);

I am confused. The proposed syntax would already accept many different types.
May 07 2014
prev sibling next sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Wed, May 07, 2014 at 08:09:34PM +0000, w0rp via Digitalmars-d wrote:
 On Wednesday, 7 May 2014 at 14:49:17 UTC, Orvid King via Digitalmars-d
 wrote:
On 5/7/14, w0rp via Digitalmars-d <digitalmars-d puremagic.com> wrote:
void foo(InputRange range);

How to make it accept multiple types? Simple, we already have template constraints, so this would be how to do it, where T is the element type of the input range: void foo(T)(InputRange!T range);

I am confused. The proposed syntax would already accept many different types.

I believe the objection is that the proposed syntax can't tell the difference between: void foo(R)(R range1, R range2) if (isInputRange!R) and void foo(R,S)(R range1, S range2) if (isInputRange!R && isInputRange!S) I.e. in the first case, the two ranges must be the same type, whereas in the second case they can be different types as long as they are both input ranges. T -- Не дорог подарок, дорога любовь.
May 07 2014
prev sibling parent "froglegs" <barf barf.com> writes:
  I believe Concepts lite in C++ works around this by allowing a 
syntax like this:

void foo(InputRange{T} range1, InputRange{T2} range2)

vs.

void foo(InputRange range1, InputRange range2)

If they are the same type.


 I believe the objection is that the proposed syntax can't tell 
 the
 difference between:

 	void foo(R)(R range1, R range2) if (isInputRange!R)

 and

 	void foo(R,S)(R range1, S range2) if (isInputRange!R && 
 isInputRange!S)

 I.e. in the first case, the two ranges must be the same type, 
 whereas in
 the second case they can be different types as long as they are 
 both
 input ranges.


 T

May 07 2014