www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why doesn't curry work with multiple arguments?

reply Andrej Mitrovic <none none.none> writes:
import std.functional;

void foo(int x, int y, int z)
{
}

alias curry!(foo, 1, 2, 3) bar;

Error: template instance curry!(foo,1,2,3) does not match template declaration
curry(alias fun,alias arg)

Shouldn't curry take a variable number of arguments and then check the length
of the arguments passed in against `fun`s length of parameters, and do its work
from there?

I'm trying to translate a C header file from the following to a D equivalent:
#define txmOpenFile(hwndTV, szFile) \

                  SendMessage((hwndTV), TXM_OPENFILE, 0, (LPARAM)(szFile))

Yes, I write a whole new function, but why do that when curry is there. Or so I
thought..
Apr 06 2011
next sibling parent reply Andrej Mitrovic <none none.none> writes:
Andrej Mitrovic Wrote:
 Yes, I write a whole new function, but why do that when curry is there. Or so
I thought..

Oops: *Yes, I _can_ write a whole new function
Apr 06 2011
next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Crap, that is a horrible implementation, I didn't take into account
not binding all arguments. Be right back..
Apr 06 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Ok, enjoy this monstrosity:

template count(T...)
{
    enum count = T.length;
}

template myCurry(alias fun, args...)
{
    static if (args.length > (ParameterTypeTuple!fun).length)
    {
        static assert(0, Format!("Tried to pass %s arguments, max is %s.",
                                 count!args, (ParameterTypeTuple!fun).length));
    }

    static if (is(typeof(fun) == delegate) || is(typeof(fun) == function))
    {
        static if ((ParameterTypeTuple!fun).length - count!args)
        {
            ReturnType!fun myCurry(ParameterTypeTuple!fun[0 ..
(ParameterTypeTuple!fun).length - count!args] myarg)
            {
                return fun(args, myarg);
            }
        }
        else
        {
            ReturnType!fun myCurry()
            {
                return fun(args);
            }
        }
    }
}
Apr 06 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
It's still wrong, the tuple is backwards. Haha, that's what I get for
not unittesting.
Apr 06 2011
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

 Here's a basic implementation:

I have some general comments: - Currying and partial function application are not exactly the same thing. So I am not sure the "curry" in std.functional is named correctly; - Partial application is important in a language that wants to support functional idioms a little. This means that if D doesn't currently supports Partial application well, then it's worth improving; - A good function application must be able to solve this little rosettacode task too: http://rosettacode.org/wiki/Partial_function_application Bye, bearophile
Apr 06 2011
parent bearophile <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

 Maybe "bind" should be a better name. I'm not sure..

In Python there is something similar that's named "partial": http://docs.python.org/library/functools.html#functools.partial Bye, bearophile
Apr 06 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Wow, talk about enlightement. I think I've done it now:

import std.stdio;
import std.traits;
import std.metastrings;

template count(T...)
{
    enum count = T.length;
}

template myCurry(alias fun, args...)
{
    static if (args.length > (ParameterTypeTuple!fun).length)
    {
        static assert(0, Format!("Tried to pass %s arguments, max is %s.",
                                 count!args, (ParameterTypeTuple!fun).length));
    }

    ReturnType!fun myCurry(T...)(T t)
    {
        return fun(args, t);
    }
}

void foo(string x, int y, int z)
{
    writeln(x, y, z);
}

alias myCurry!(foo, "bar")       oneCurry;
alias myCurry!(foo, "bar", 1)    twoCurry;
alias myCurry!(foo, "bar", 1, 2) threeCurry;

void main()
{
    oneCurry(1, 2);
    twoCurry(2);
    threeCurry();
}
Apr 06 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 4/6/11, bearophile <bearophileHUGS lycos.com> wrote:
 - Currying and partial function application are not exactly the same thing.
 So I am not sure the "curry" in std.functional is named correctly;

Maybe "bind" should be a better name. I'm not sure..
Apr 06 2011
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Well this still hasn't solved my problem. Because I shouldn't bind
from left to right, but arbitrarily.

So ideally I would want this:

void foo(string str, int x, int y, string str2) { }

alias bind!(foo, null, 1, 2, null) twoStrings;
twoStrings("abc", "def");   ->   foo("abc", 1, 2, "def");

I'm pretty sure I could do this with D's templates.
Apr 06 2011
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Here's a basic implementation:

import std.stdio;
import std.traits;
import std.metastrings;

template count(T...)
{
    enum count = T.length;
}

template curry(alias fun, args...)
{
    static if (args.length > (ParameterTypeTuple!fun).length)
    {
        static assert(0, Format!("Tried to pass %s arguments, max is %s.",
                                 count!args, (ParameterTypeTuple!fun).length));
    }

    static if (is(typeof(fun) == delegate) || is(typeof(fun) == function))
    {
        ReturnType!fun curry()
        {
            return fun(args);
        }
    }
}

void foo(int x, int y)
{
    writeln(x, y);
}

alias curry!(foo, 1, 2) bar;

void main()
{
	bar();
}

It will complain if you try to pass it more arguments than a function
can take. I didn't implement curry's original else clause because I
have no idea what's going on there (some comments would be useful in
Phobos implementations, people!......)

There is some wacky error if I didn't use the count template
workaround. If I try to use args.length twice, like so:
    static if (args.length > (ParameterTypeTuple!fun).length)
    {
        static assert(0, Format!("Tried to pass %s arguments, max is %s.",
                                 args.length, (ParameterTypeTuple!fun).length));
    }

Then I get back: identifier 'length' of 'args.length' is not defined

But it only errors out in the static assert, and yet it can check
args.length in the static if. Really weird.
Apr 06 2011