Jump to page: 1 2 3
Thread overview
possible "solution" for ufcs
Jun 06, 2011
Monkol
Jun 06, 2011
KennyTM~
Jun 06, 2011
so
Jun 06, 2011
KennyTM~
Jun 06, 2011
KennyTM~
Jun 06, 2011
Timon Gehr
Jun 06, 2011
Torarin
Jun 06, 2011
Timon Gehr
Jun 06, 2011
Michel Fortin
Jun 06, 2011
Timon Gehr
Jun 06, 2011
Monkol
Jun 06, 2011
Monkol
Jun 06, 2011
Mafi
Jun 06, 2011
Lutger Blijdestijn
Jun 06, 2011
Lutger Blijdestijn
Jun 06, 2011
Nick Sabalausky
Jun 07, 2011
Nick Sabalausky
June 06, 2011
Someone wrote a very compelling argument for ufcs (uniform function call syntax) for ranges, and that is, given a slew of range functions, and a slew of ranges, it is nice to use a fluent programming syntax to specify wrappers for ranges without having to extend each range type.  For example:

take(10,stride(2,cycle([3,2,5,3])));

vs.

[3,2,5,3].cycle().stride(2).take(10);

And I thought damn it would be nice if ranges could implement ufcs, but other types that you didn't want to allow infinite extendability could avoid it.  That gave me an idea :)


import std.stdio;

struct ufcs
{
    auto opDispatch(string name, T...)(T args) // appropriate if compiles constraint here
    {
        mixin("return ." ~ name ~ "(this, args);");
    }
}

int foo(ufcs x, int y)
{
    writefln("it works! %d", y);
    return y+1;
}

void main()
{
    ufcs u;
    auto x = u.foo(1);
    assert(x == 2);
}

And it does indeed work (2.053)...

So we can have ufcs without any changes to the compiler, and we also make it a *choice* for people who don't want to allow infinite extendability, and don't want to deal with possible compiler ambiguities.

The opDispatch could even be a mixin itself (I think).

What do you think?

-Steve
June 06, 2011
On Mon, 06 Jun 2011 22:00:13 +0300, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> Someone wrote a very compelling argument for ufcs (uniform function call syntax) for ranges, and that is, given a slew of range functions, and a slew of ranges, it is nice to use a fluent programming syntax to specify wrappers for ranges without having to extend each range type.  For example:
>
> take(10,stride(2,cycle([3,2,5,3])));
>
> vs.
>
> [3,2,5,3].cycle().stride(2).take(10);
>
> And I thought damn it would be nice if ranges could implement ufcs, but other types that you didn't want to allow infinite extendability could avoid it.  That gave me an idea :)
>
>
> import std.stdio;
>
> struct ufcs
> {
>      auto opDispatch(string name, T...)(T args) // appropriate if compiles constraint here
>      {
>          mixin("return ." ~ name ~ "(this, args);");
>      }
> }
>
> int foo(ufcs x, int y)
> {
>      writefln("it works! %d", y);
>      return y+1;
> }
>
> void main()
> {
>      ufcs u;
>      auto x = u.foo(1);
>      assert(x == 2);
> }
>
> And it does indeed work (2.053)...
>
> So we can have ufcs without any changes to the compiler, and we also make it a *choice* for people who don't want to allow infinite extendability, and don't want to deal with possible compiler ambiguities.
>
> The opDispatch could even be a mixin itself (I think).
>
> What do you think?
>
> -Steve


may be you lost symbol "!" when call template opDispatch!(string name, T...)(T args)
June 06, 2011
On Jun 7, 11 03:22, Monkol wrote:
> On Mon, 06 Jun 2011 22:00:13 +0300, Steven Schveighoffer
> <schveiguy@yahoo.com> wrote:
>
>> Someone wrote a very compelling argument for ufcs (uniform function
>> call syntax) for ranges, and that is, given a slew of range functions,
>> and a slew of ranges, it is nice to use a fluent programming syntax to
>> specify wrappers for ranges without having to extend each range type.
>> For example:
>>
>> take(10,stride(2,cycle([3,2,5,3])));
>>
>> vs.
>>
>> [3,2,5,3].cycle().stride(2).take(10);
>>
>> And I thought damn it would be nice if ranges could implement ufcs,
>> but other types that you didn't want to allow infinite extendability
>> could avoid it. That gave me an idea :)
>>
>>
>> import std.stdio;
>>
>> struct ufcs
>> {
>> auto opDispatch(string name, T...)(T args) // appropriate if compiles
>> constraint here
>> {
>> mixin("return ." ~ name ~ "(this, args);");
>> }
>> }
>>
>> int foo(ufcs x, int y)
>> {
>> writefln("it works! %d", y);
>> return y+1;
>> }
>>
>> void main()
>> {
>> ufcs u;
>> auto x = u.foo(1);
>> assert(x == 2);
>> }
>>
>> And it does indeed work (2.053)...
>>
>> So we can have ufcs without any changes to the compiler, and we also
>> make it a *choice* for people who don't want to allow infinite
>> extendability, and don't want to deal with possible compiler ambiguities.
>>
>> The opDispatch could even be a mixin itself (I think).
>>
>> What do you think?
>>
>> -Steve
>
>
> may be you lost symbol "!" when call template opDispatch!(string name,
> T...)(T args)

No Steven is correct. You don't need the '!' before the template parameters.
June 06, 2011
I think something is missing here.

It doesn't convert fun(int, ufcs) to ufcs.fun(int) as in the examples.
It converts fun(ufcs, int) to ufcs.fun(int).

We need is a solution to this:

fun(T)(arg1, ... ufcs!T, ... argN)
June 06, 2011
On Jun 7, 11 03:00, Steven Schveighoffer wrote:
> Someone wrote a very compelling argument for ufcs (uniform function call
> syntax) for ranges, and that is, given a slew of range functions, and a
> slew of ranges, it is nice to use a fluent programming syntax to specify
> wrappers for ranges without having to extend each range type. For example:
>
> take(10,stride(2,cycle([3,2,5,3])));
>
> vs.
>
> [3,2,5,3].cycle().stride(2).take(10);
>
> And I thought damn it would be nice if ranges could implement ufcs, but
> other types that you didn't want to allow infinite extendability could
> avoid it. That gave me an idea :)
>
>
> import std.stdio;
>
> struct ufcs
> {
> auto opDispatch(string name, T...)(T args) // appropriate if compiles
> constraint here
> {
> mixin("return ." ~ name ~ "(this, args);");
> }
> }
>
> int foo(ufcs x, int y)
> {
> writefln("it works! %d", y);
> return y+1;
> }
>
> void main()
> {
> ufcs u;
> auto x = u.foo(1);
> assert(x == 2);
> }
>
> And it does indeed work (2.053)...
>
> So we can have ufcs without any changes to the compiler, and we also
> make it a *choice* for people who don't want to allow infinite
> extendability, and don't want to deal with possible compiler ambiguities.
>
> The opDispatch could even be a mixin itself (I think).
>
> What do you think?
>
> -Steve

Maybe better

    auto ref opDispatch(string name, T...)(auto ref T args) {
        mixin("return ." ~ name ~ "(this, args);");
    }

so that ref-returns and ref-parameters can be handled as well. Doesn't work for 'lazy' though. It also cannot preserve 'pure'-ity, 'nothrow'-ness and '@safe'-ty of the original function.

June 06, 2011
Steven Schveighoffer wrote:
> Someone wrote a very compelling argument for ufcs (uniform function call syntax) for ranges, and that is, given a slew of range functions, and a slew of ranges, it is nice to use a fluent programming syntax to specify wrappers for ranges without having to extend each range type.  For example:
>
> take(10,stride(2,cycle([3,2,5,3])));
>
> vs.
>
> [3,2,5,3].cycle().stride(2).take(10);
>
> And I thought damn it would be nice if ranges could implement ufcs, but other types that you didn't want to allow infinite extendability could avoid it.  That gave me an idea :)
>
>
> import std.stdio;
>
> struct ufcs
> {
>      auto opDispatch(string name, T...)(T args) // appropriate if compiles
> constraint here
>      {
>          mixin("return ." ~ name ~ "(this, args);");
>      }
> }
>
> int foo(ufcs x, int y)
> {
>      writefln("it works! %d", y);
>      return y+1;
> }
>
> void main()
> {
>      ufcs u;
>      auto x = u.foo(1);
>      assert(x == 2);
> }
>
> And it does indeed work (2.053)...
>
> So we can have ufcs without any changes to the compiler, and we also make it a *choice* for people who don't want to allow infinite extendability, and don't want to deal with possible compiler ambiguities.
>
> The opDispatch could even be a mixin itself (I think).
>
> What do you think?
>
> -Steve

Great! =) This resolves everything around UFCS!

Why has nobody come up with this before? You should definitely file an enhancement request for phobos.

We just add something like this somewhere:

mixin template implementUFCS() {
    auto opDispatch(string name, T...)(T args) if(is(typeof({mixin("return ." ~
name ~ "(this, args);");}))){
        mixin("return ." ~ name ~ "(this, args);");
    }
}

Each range type will do

    mixin implementUFCS;


And we'll have optional UFCS!!!

A little drawback: Types using implementUFCS will have very bad error reporting if somebody types a member name wrong:

struct foo{mixin implementUFCS;}
int main(){foo x;x.bar();}

Error: template instance opDispatch!("bar") does not match template declaration
opDispatch(string name,T...) if (is(typeof(delegate ()
{
mixin("return ." ~ name ~ "(this, args);");
}
)))

I think here a change to the compiler would be appropriate at some point so that
we can get:
Error: no property 'bar' for type 'foo'

If opDispatch does not match.

But this is definitely the way to go for UFCS!

Timon


June 06, 2011
On 2011-06-06 15:00:13 -0400, "Steven Schveighoffer" <schveiguy@yahoo.com> said:

> And it does indeed work (2.053)...
> 
> So we can have ufcs without any changes to the compiler, and we also make  it a *choice* for people who don't want to allow infinite extendability,  and don't want to deal with possible compiler ambiguities.
> 
> The opDispatch could even be a mixin itself (I think).
> 
> What do you think?

Clever. But how does it work for properties? Pure/safe/nothrow functions? Ref and out parameters? Note that properties specifically are already problem for the compiler-implemented array-member syntax.

Bottom line: there's a lot of work to do to make UFCS work right. And it'll require some language-level changes anyway if we want it to work right.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

June 06, 2011
On Mon, 06 Jun 2011 22:00:13 +0300, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> Someone wrote a very compelling argument for ufcs (uniform function call syntax) for ranges, and that is, given a slew of range functions, and a slew of ranges, it is nice to use a fluent programming syntax to specify wrappers for ranges without having to extend each range type.  For example:
>
> take(10,stride(2,cycle([3,2,5,3])));
>
> vs.
>
> [3,2,5,3].cycle().stride(2).take(10);
>
> And I thought damn it would be nice if ranges could implement ufcs, but other types that you didn't want to allow infinite extendability could avoid it.  That gave me an idea :)
>
>
> import std.stdio;
>
> struct ufcs
> {
>      auto opDispatch(string name, T...)(T args) // appropriate if compiles constraint here
>      {
>          mixin("return ." ~ name ~ "(this, args);");
>      }
> }
>
> int foo(ufcs x, int y)
> {
>      writefln("it works! %d", y);
>      return y+1;
> }
>
> void main()
> {
>      ufcs u;
>      auto x = u.foo(1);
>      assert(x == 2);
> }
>
> And it does indeed work (2.053)...
>
> So we can have ufcs without any changes to the compiler, and we also make it a *choice* for people who don't want to allow infinite extendability, and don't want to deal with possible compiler ambiguities.
>
> The opDispatch could even be a mixin itself (I think).
>
> What do you think?
>
> -Steve

what this code must to do?
June 06, 2011
On Mon, 06 Jun 2011 22:00:13 +0300, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> Someone wrote a very compelling argument for ufcs (uniform function call syntax) for ranges, and that is, given a slew of range functions, and a slew of ranges, it is nice to use a fluent programming syntax to specify wrappers for ranges without having to extend each range type.  For example:
>
> take(10,stride(2,cycle([3,2,5,3])));
>
> vs.
>
> [3,2,5,3].cycle().stride(2).take(10);
>
> And I thought damn it would be nice if ranges could implement ufcs, but other types that you didn't want to allow infinite extendability could avoid it.  That gave me an idea :)
>
>
> import std.stdio;
>
> struct ufcs
> {
>      auto opDispatch(string name, T...)(T args) // appropriate if compiles constraint here
>      {
>          mixin("return ." ~ name ~ "(this, args);");
>      }
> }
>
> int foo(ufcs x, int y)
> {
>      writefln("it works! %d", y);
>      return y+1;
> }
>
> void main()
> {
>      ufcs u;
>      auto x = u.foo(1);
>      assert(x == 2);
> }
>
> And it does indeed work (2.053)...
>
> So we can have ufcs without any changes to the compiler, and we also make it a *choice* for people who don't want to allow infinite extendability, and don't want to deal with possible compiler ambiguities.
>
> The opDispatch could even be a mixin itself (I think).
>
> What do you think?
>
> -Steve


what this code must to do?
June 06, 2011
On Jun 7, 11 03:23, so wrote:
> I think something is missing here.
>
> It doesn't convert fun(int, ufcs) to ufcs.fun(int) as in the examples.
> It converts fun(ufcs, int) to ufcs.fun(int).
>
> We need is a solution to this:
>
> fun(T)(arg1, ... ufcs!T, ... argN)

    auto ref opDispatch(string name, T...)(auto ref T args) {
        mixin("alias ." ~ name ~ " f;");
        alias ParameterTypeTuple!f Params;
        enum i = staticIndexOf!(Unqual!(typeof(this)), staticMap!(Unqual, Params));
        static assert(i >= 0);
        return f(args[0 .. i], this, args[i .. $]);
    }

(Doesn't work with overload set though.)
« First   ‹ Prev
1 2 3