www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Partial template application

reply bearophile <bearophileHUGS lycos.com> writes:
Functional languages often have built-in partial function application:
http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application

D doesn't have this feature built-in despite it can't break C compatibility,
maybe because it's a lot of work to implement, maybe because it is seen as
bug-prone, or maybe just because Walter was/is not interested in it.

So in D you do it using std.functional.curry (I think this is a partial
application more than a currying, so I am not sure this is the best name):

int fun(int a, int b) { return a + b; }
alias curry!(fun, 5) fun5;
assert(fun5(6) == 11);


Few hours ago in D.learn Simen Kjaeraas has shown a small D2 program that uses
nested templates to allow template partial application:


import std.typecons, std.functional, std.array;

template optArg(alias pred) {
    template optArg(alias fn) {
        auto optArg(Range)(Range r) {
           alias binaryFun!pred predicate;
           alias unaryFun!fn func;

           auto result = tuple(r.front, func(r.front));
           foreach (e; r) {
              auto tmp = func(e);
              if (predicate(e, result[1]))
                 result = tuple(e, tmp);
           }
           return result;
        }
    }
}
void main() {
    alias optArg!"a < b" minArg;
    alias minArg!"a" foo;
    assert(foo([5, 2, 1, 3]) == tuple(1, 1));
}


I'd like a new template feature in D, automatic partial template application.
It allows to simplify that code like this (untested):

import std.typecons, std.functional, std.array;

auto optArg(alias pred, alias fn, Range)(Range r) {
    alias binaryFun!pred predicate;
    alias unaryFun!fn func;

    auto result = tuple(r.front, func(r.front));
    foreach (e; r) {
        auto tmp = func(e);
        if (predicate(e, result[1]))
            result = tuple(e, tmp);
    }
}

void main() {
    alias optArg!"a < b" minArg;
    alias minArg!"a" foo;
    assert(foo([5, 2, 1, 3]) == tuple(1, 1));
}


If (as usual) you don't want to implement this as a new D feature, I presume
something like a PartialTemplate/StaticCurry/StaticPartialApplication is
writeable in the standard library:

import std.typecons, std.functional, std.array;

auto optArg(alias pred, alias fn, Range)(Range r) {
    alias binaryFun!pred predicate;
    alias unaryFun!fn func;

    auto result = tuple(r.front, func(r.front));
    foreach (e; r) {
        auto tmp = func(e);
        if (predicate(e, result[1]))
            result = tuple(e, tmp);
    }
}

void main() {
    alias PartialTemplate!(optArg, "a < b") minArg;
    alias PartialTemplate!(minArg, "a") foo;
    assert(foo([5, 2, 1, 3]) == tuple(1, 1));
}

Bye,
bearophile
Jan 31 2011
next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
bearophile <bearophileHUGS lycos.com> wrote:

 If (as usual) you don't want to implement this as a new D feature, I  
 presume something like a  
 PartialTemplate/StaticCurry/StaticPartialApplication is writeable in the  
 standard library:

 import std.typecons, std.functional, std.array;

 auto optArg(alias pred, alias fn, Range)(Range r) {
     alias binaryFun!pred predicate;
     alias unaryFun!fn func;

     auto result = tuple(r.front, func(r.front));
     foreach (e; r) {
         auto tmp = func(e);
         if (predicate(e, result[1]))
             result = tuple(e, tmp);
     }
 }

 void main() {
     alias PartialTemplate!(optArg, "a < b") minArg;
     alias PartialTemplate!(minArg, "a") foo;
     assert(foo([5, 2, 1, 3]) == tuple(1, 1));
 }

dranges has something like this: http://svn.dsource.org/projects/dranges/trunk/dranges/docs/templates.html see CurryTemplate -- Simen
Jan 31 2011
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Isn't it possible to unify functions which work on compile-time and
run-time arguments?

What I mean is, if curry detects that you're currying a compile-time
argument against a template, then it should do what your
PartialTemplate/StaticCurry would do.

It seems odd having to create 'static' counterparts to functions in
Phobos. So far I only know of staticIndexOf and staticMap. But adding
more would seem like duplicating the effort and creating a sort of
std.staticAlgorithm
Jan 31 2011
parent bearophile <bearophileHUGS lycos.com> writes:
Andrej Mitrovic:

 What I mean is, if curry detects that you're currying a compile-time
 argument against a template, then it should do what your
 PartialTemplate/StaticCurry would do.

This is currently not possible, a static curry needs compile-time arguments, so they need to specified as template arguments (with the bang !), while the normal curry is supposed to work with run-time values too. Walter has tried to unify the two, using a syntax like: void foo(int x, static int y, int z) {} That's equivalent to: void foo(int y)(int x, int z) {} But the legend says he has found unspecified implementation difficulties, so the idea was abandoned. One purpose of D design is to avoid excessive compiler complexity. ----------------------- Simen kjaeraas:
 dranges has something like this:
 http://svn.dsource.org/projects/dranges/trunk/dranges/docs/templates.html
 see CurryTemplate

Thank you for the link. This looks like a true currying, instead of a partial application like std.functional.curry. I suggest to add CurryTemplate to Phobos2 while we wait forever for templates to be curried on default :-) Bye, bearophile
Jan 31 2011
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 1/31/11, bearophile <bearophileHUGS lycos.com> wrote:
 Walter has tried to unify the two, using a syntax like:
 void foo(int x, static int y, int z) {}
 That's equivalent to:
 void foo(int y)(int x, int z) {}

 But the legend says he has found unspecified implementation difficulties, so
 the idea was abandoned. One purpose of D design is to avoid excessive
 compiler complexity.

Well I do think there should be a difference between compile time and runtime arguments, so I'd keep the current syntax rather than mix the two. Perhaps D3 could have a form of static expressions: import std.typecons, std.functional, std.array; auto optArg(alias pred, alias fn, Range)(Range r) { // same as before } void main() { alias static optArg!"a < b" minArg; alias static minArg!"a" foo; assert(foo([5, 2, 1, 3]) == tuple(1, 1)); } and then we could have: import std.stdio, std.typetuple, std.algorithm, std.traits, std.range; void main() { int[] arr1 = [ 1, 2, 3, 4 ]; int[] arr2 = [ 5, 6 ]; auto squares = map!("a * a")(chain(arr1, arr2)); //~ alias staticMap!(Unqual, int, const int, immutable int) T; alias static map!(Unqual, int, const int, immutable int) T; } I'm not sure how that would work, I'm just throwing ideas in the air. :)
Jan 31 2011