// Written in the D programming language. /** Functions that manipulate other functions. This module provides functions for compile time function composition. These functions are helpful when constructing predicates for the algorithms in $(MREF std, algorithm) or $(MREF std, range). $(SCRIPT inhibitQuickIndex = 1;) $(DIVC quickindex, $(BOOKTABLE , $(TR $(TH Function Name) $(TH Description) ) $(TR $(TD $(LREF adjoin)) $(TD Joins a couple of functions into one that executes the original functions independently and returns a tuple with all the results. )) $(TR $(TD $(LREF compose), $(LREF pipe)) $(TD Join a couple of functions into one that executes the original functions one after the other, using one function's result for the next function's argument. )) $(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo)) $(TD Ready-made predicate functions to compare two values. )) $(TR $(TD $(LREF memoize)) $(TD Creates a function that caches its result for fast re-evaluation. )) $(TR $(TD $(LREF not)) $(TD Creates a function that negates another. )) $(TR $(TD $(LREF partial)) $(TD Creates a function that binds the first argument of a given function to a given value. )) $(TR $(TD $(LREF curry)) $(TD Converts a multi-argument function into a series of single-argument functions. f(x, y) == curry(f)(x)(y) )) $(TR $(TD $(LREF reverseArgs)) $(TD Predicate that reverses the order of its arguments. )) $(TR $(TD $(LREF toDelegate)) $(TD Converts a callable to a delegate. )) $(TR $(TD $(LREF unaryFun), $(LREF binaryFun)) $(TD Create a unary or binary function from a string. Most often used when defining algorithms on ranges. )) )) Copyright: Copyright Andrei Alexandrescu 2008 - 2009. License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.org, Andrei Alexandrescu) Source: $(PHOBOSSRC std/functional.d) */ /* Copyright Andrei Alexandrescu 2008 - 2009. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ module std.functional; import std.meta : AliasSeq, Reverse; import std.traits : isCallable, Parameters; import std.conv : toCtString; import std.internal.attributes : betterC; public import core.lifetime : forward; private template needOpCallAlias(alias fun) { /* Determine whether or not unaryFun and binaryFun need to alias to fun or * fun.opCall. Basically, fun is a function object if fun(...) compiles. We * want is(unaryFun!fun) (resp., is(binaryFun!fun)) to be true if fun is * any function object. There are 4 possible cases: * * 1) fun is the type of a function object with static opCall; * 2) fun is an instance of a function object with static opCall; * 3) fun is the type of a function object with non-static opCall; * 4) fun is an instance of a function object with non-static opCall. * * In case (1), is(unaryFun!fun) should compile, but does not if unaryFun * aliases itself to fun, because typeof(fun) is an error when fun itself * is a type. So it must be aliased to fun.opCall instead. All other cases * should be aliased to fun directly. */ static if (is(typeof(fun.opCall) == function)) { enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () { return fun(Parameters!fun.init); }); } else enum needOpCallAlias = false; } /** Transforms a `string` representing an expression into a unary function. The `string` must either use symbol name `a` as the parameter or provide the symbol via the `parmName` argument. Params: fun = a `string` or a callable parmName = the name of the parameter if `fun` is a string. Defaults to `"a"`. Returns: If `fun` is a `string`, a new single parameter function If `fun` is not a `string`, an alias to `fun`. */ template unaryFun(alias fun, string parmName = "a") { static if (is(typeof(fun) : string)) { static if (!fun._ctfeMatchUnary(parmName)) { import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; import std.meta, std.traits, std.typecons; } auto unaryFun(ElementType)(auto ref ElementType __a) { mixin("alias " ~ parmName ~ " = __a ;"); return mixin(fun); } } else static if (needOpCallAlias!fun) { // https://issues.dlang.org/show_bug.cgi?id=9906 alias unaryFun = fun.opCall; } else { alias unaryFun = fun; } } /// @safe unittest { // Strings are compiled into functions: alias isEven = unaryFun!("(a & 1) == 0"); assert(isEven(2) && !isEven(1)); } @safe unittest { static int f1(int a) { return a + 1; } static assert(is(typeof(unaryFun!(f1)(1)) == int)); assert(unaryFun!(f1)(41) == 42); int f2(int a) { return a + 1; } static assert(is(typeof(unaryFun!(f2)(1)) == int)); assert(unaryFun!(f2)(41) == 42); assert(unaryFun!("a + 1")(41) == 42); //assert(unaryFun!("return a + 1;")(41) == 42); int num = 41; assert(unaryFun!"a + 1"(num) == 42); // https://issues.dlang.org/show_bug.cgi?id=9906 struct Seen { static bool opCall(int n) { return true; } } static assert(needOpCallAlias!Seen); static assert(is(typeof(unaryFun!Seen(1)))); assert(unaryFun!Seen(1)); Seen s; static assert(!needOpCallAlias!s); static assert(is(typeof(unaryFun!s(1)))); assert(unaryFun!s(1)); struct FuncObj { bool opCall(int n) { return true; } } FuncObj fo; static assert(!needOpCallAlias!fo); static assert(is(typeof(unaryFun!fo))); assert(unaryFun!fo(1)); // Function object with non-static opCall can only be called with an // instance, not with merely the type. static assert(!is(typeof(unaryFun!FuncObj))); } /** Transforms a `string` representing an expression into a binary function. The `string` must either use symbol names `a` and `b` as the parameters or provide the symbols via the `parm1Name` and `parm2Name` arguments. Params: fun = a `string` or a callable parm1Name = the name of the first parameter if `fun` is a string. Defaults to `"a"`. parm2Name = the name of the second parameter if `fun` is a string. Defaults to `"b"`. Returns: If `fun` is not a string, `binaryFun` aliases itself away to `fun`. */ template binaryFun(alias fun, string parm1Name = "a", string parm2Name = "b") { static if (is(typeof(fun) : string)) { static if (!fun._ctfeMatchBinary(parm1Name, parm2Name)) { import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; import std.meta, std.traits, std.typecons; } auto binaryFun(ElementType1, ElementType2) (auto ref ElementType1 __a, auto ref ElementType2 __b) { mixin("alias "~parm1Name~" = __a ;"); mixin("alias "~parm2Name~" = __b ;"); return mixin(fun); } } else static if (needOpCallAlias!fun) { // https://issues.dlang.org/show_bug.cgi?id=9906 alias binaryFun = fun.opCall; } else { alias binaryFun = fun; } } /// @safe unittest { alias less = binaryFun!("a < b"); assert(less(1, 2) && !less(2, 1)); alias greater = binaryFun!("a > b"); assert(!greater("1", "2") && greater("2", "1")); } @safe unittest { static int f1(int a, string b) { return a + 1; } static assert(is(typeof(binaryFun!(f1)(1, "2")) == int)); assert(binaryFun!(f1)(41, "a") == 42); string f2(int a, string b) { return b ~ "2"; } static assert(is(typeof(binaryFun!(f2)(1, "1")) == string)); assert(binaryFun!(f2)(1, "4") == "42"); assert(binaryFun!("a + b")(41, 1) == 42); //@@BUG //assert(binaryFun!("return a + b;")(41, 1) == 42); // https://issues.dlang.org/show_bug.cgi?id=9906 struct Seen { static bool opCall(int x, int y) { return true; } } static assert(is(typeof(binaryFun!Seen))); assert(binaryFun!Seen(1,1)); struct FuncObj { bool opCall(int x, int y) { return true; } } FuncObj fo; static assert(!needOpCallAlias!fo); static assert(is(typeof(binaryFun!fo))); assert(unaryFun!fo(1,1)); // Function object with non-static opCall can only be called with an // instance, not with merely the type. static assert(!is(typeof(binaryFun!FuncObj))); } // skip all ASCII chars except a .. z, A .. Z, 0 .. 9, '_' and '.'. private uint _ctfeSkipOp(ref string op) { if (!__ctfe) assert(false); import std.ascii : isASCII, isAlphaNum; immutable oldLength = op.length; while (op.length) { immutable front = op[0]; if (front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.')) op = op[1..$]; else break; } return oldLength != op.length; } // skip all digits private uint _ctfeSkipInteger(ref string op) { if (!__ctfe) assert(false); import std.ascii : isDigit; immutable oldLength = op.length; while (op.length) { immutable front = op[0]; if (front.isDigit()) op = op[1..$]; else break; } return oldLength != op.length; } // skip name private uint _ctfeSkipName(ref string op, string name) { if (!__ctfe) assert(false); if (op.length >= name.length && op[0 .. name.length] == name) { op = op[name.length..$]; return 1; } return 0; } // returns 1 if `fun` is trivial unary function private uint _ctfeMatchUnary(string fun, string name) { if (!__ctfe) assert(false); fun._ctfeSkipOp(); for (;;) { immutable h = fun._ctfeSkipName(name) + fun._ctfeSkipInteger(); if (h == 0) { fun._ctfeSkipOp(); break; } else if (h == 1) { if (!fun._ctfeSkipOp()) break; } else return 0; } return fun.length == 0; } @safe unittest { static assert(!_ctfeMatchUnary("sqrt(ё)", "ё")); static assert(!_ctfeMatchUnary("ё.sqrt", "ё")); static assert(!_ctfeMatchUnary(".ё+ё", "ё")); static assert(!_ctfeMatchUnary("_ё+ё", "ё")); static assert(!_ctfeMatchUnary("ёё", "ё")); static assert(_ctfeMatchUnary("a+a", "a")); static assert(_ctfeMatchUnary("a + 10", "a")); static assert(_ctfeMatchUnary("4 == a", "a")); static assert(_ctfeMatchUnary("2 == a", "a")); static assert(_ctfeMatchUnary("1 != a", "a")); static assert(_ctfeMatchUnary("a != 4", "a")); static assert(_ctfeMatchUnary("a< 1", "a")); static assert(_ctfeMatchUnary("434 < a", "a")); static assert(_ctfeMatchUnary("132 > a", "a")); static assert(_ctfeMatchUnary("123 >a", "a")); static assert(_ctfeMatchUnary("a>82", "a")); static assert(_ctfeMatchUnary("ё>82", "ё")); static assert(_ctfeMatchUnary("ё[ё(ё)]", "ё")); static assert(_ctfeMatchUnary("ё[21]", "ё")); } // returns 1 if `fun` is trivial binary function private uint _ctfeMatchBinary(string fun, string name1, string name2) { if (!__ctfe) assert(false); fun._ctfeSkipOp(); for (;;) { immutable h = fun._ctfeSkipName(name1) + fun._ctfeSkipName(name2) + fun._ctfeSkipInteger(); if (h == 0) { fun._ctfeSkipOp(); break; } else if (h == 1) { if (!fun._ctfeSkipOp()) break; } else return 0; } return fun.length == 0; } @safe unittest { static assert(!_ctfeMatchBinary("sqrt(ё)", "ё", "b")); static assert(!_ctfeMatchBinary("ё.sqrt", "ё", "b")); static assert(!_ctfeMatchBinary(".ё+ё", "ё", "b")); static assert(!_ctfeMatchBinary("_ё+ё", "ё", "b")); static assert(!_ctfeMatchBinary("ёё", "ё", "b")); static assert(_ctfeMatchBinary("a+a", "a", "b")); static assert(_ctfeMatchBinary("a + 10", "a", "b")); static assert(_ctfeMatchBinary("4 == a", "a", "b")); static assert(_ctfeMatchBinary("2 == a", "a", "b")); static assert(_ctfeMatchBinary("1 != a", "a", "b")); static assert(_ctfeMatchBinary("a != 4", "a", "b")); static assert(_ctfeMatchBinary("a< 1", "a", "b")); static assert(_ctfeMatchBinary("434 < a", "a", "b")); static assert(_ctfeMatchBinary("132 > a", "a", "b")); static assert(_ctfeMatchBinary("123 >a", "a", "b")); static assert(_ctfeMatchBinary("a>82", "a", "b")); static assert(_ctfeMatchBinary("ё>82", "ё", "q")); static assert(_ctfeMatchBinary("ё[ё(10)]", "ё", "q")); static assert(_ctfeMatchBinary("ё[21]", "ё", "q")); static assert(!_ctfeMatchBinary("sqrt(ё)+b", "b", "ё")); static assert(!_ctfeMatchBinary("ё.sqrt-b", "b", "ё")); static assert(!_ctfeMatchBinary(".ё+b", "b", "ё")); static assert(!_ctfeMatchBinary("_b+ё", "b", "ё")); static assert(!_ctfeMatchBinary("ba", "b", "a")); static assert(_ctfeMatchBinary("a+b", "b", "a")); static assert(_ctfeMatchBinary("a + b", "b", "a")); static assert(_ctfeMatchBinary("b == a", "b", "a")); static assert(_ctfeMatchBinary("b == a", "b", "a")); static assert(_ctfeMatchBinary("b != a", "b", "a")); static assert(_ctfeMatchBinary("a != b", "b", "a")); static assert(_ctfeMatchBinary("a< b", "b", "a")); static assert(_ctfeMatchBinary("b < a", "b", "a")); static assert(_ctfeMatchBinary("b > a", "b", "a")); static assert(_ctfeMatchBinary("b >a", "b", "a")); static assert(_ctfeMatchBinary("a>b", "b", "a")); static assert(_ctfeMatchBinary("ё>b", "b", "ё")); static assert(_ctfeMatchBinary("b[ё(-1)]", "b", "ё")); static assert(_ctfeMatchBinary("ё[-21]", "b", "ё")); } //undocumented template safeOp(string S) if (S=="<"||S==">"||S=="<="||S==">="||S=="=="||S=="!=") { import std.traits : isIntegral; private bool unsafeOp(ElementType1, ElementType2)(ElementType1 a, ElementType2 b) pure if (isIntegral!ElementType1 && isIntegral!ElementType2) { import std.traits : CommonType; alias T = CommonType!(ElementType1, ElementType2); return mixin("cast(T)a "~S~" cast(T) b"); } bool safeOp(T0, T1)(auto ref T0 a, auto ref T1 b) { import std.traits : mostNegative; static if (isIntegral!T0 && isIntegral!T1 && (mostNegative!T0 < 0) != (mostNegative!T1 < 0)) { static if (S == "<=" || S == "<") { static if (mostNegative!T0 < 0) immutable result = a < 0 || unsafeOp(a, b); else immutable result = b >= 0 && unsafeOp(a, b); } else { static if (mostNegative!T0 < 0) immutable result = a >= 0 && unsafeOp(a, b); else immutable result = b < 0 || unsafeOp(a, b); } } else { static assert(is(typeof(mixin("a "~S~" b"))), "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ "."); immutable result = mixin("a "~S~" b"); } return result; } } @safe unittest //check user defined types { import std.algorithm.comparison : equal; struct Foo { int a; auto opEquals(Foo foo) { return a == foo.a; } } assert(safeOp!"!="(Foo(1), Foo(2))); } /** Predicate that returns $(D_PARAM a < b). Correctly compares signed and unsigned integers, ie. -1 < 2U. */ alias lessThan = safeOp!"<"; /// pure @safe @nogc nothrow unittest { assert(lessThan(2, 3)); assert(lessThan(2U, 3U)); assert(lessThan(2, 3.0)); assert(lessThan(-2, 3U)); assert(lessThan(2, 3U)); assert(!lessThan(3U, -2)); assert(!lessThan(3U, 2)); assert(!lessThan(0, 0)); assert(!lessThan(0U, 0)); assert(!lessThan(0, 0U)); } /** Predicate that returns $(D_PARAM a > b). Correctly compares signed and unsigned integers, ie. 2U > -1. */ alias greaterThan = safeOp!">"; /// @safe unittest { assert(!greaterThan(2, 3)); assert(!greaterThan(2U, 3U)); assert(!greaterThan(2, 3.0)); assert(!greaterThan(-2, 3U)); assert(!greaterThan(2, 3U)); assert(greaterThan(3U, -2)); assert(greaterThan(3U, 2)); assert(!greaterThan(0, 0)); assert(!greaterThan(0U, 0)); assert(!greaterThan(0, 0U)); } /** Predicate that returns $(D_PARAM a == b). Correctly compares signed and unsigned integers, ie. !(-1 == ~0U). */ alias equalTo = safeOp!"=="; /// @safe unittest { assert(equalTo(0U, 0)); assert(equalTo(0, 0U)); assert(!equalTo(-1, ~0U)); } /** N-ary predicate that reverses the order of arguments, e.g., given $(D pred(a, b, c)), returns $(D pred(c, b, a)). Params: pred = A callable Returns: A function which calls `pred` after reversing the given parameters */ template reverseArgs(alias pred) { auto reverseArgs(Args...)(auto ref Args args) if (is(typeof(pred(Reverse!args)))) { return pred(Reverse!args); } } /// @safe unittest { alias gt = reverseArgs!(binaryFun!("a < b")); assert(gt(2, 1) && !gt(1, 1)); } /// @safe unittest { int x = 42; bool xyz(int a, int b) { return a * x < b / x; } auto foo = &xyz; foo(4, 5); alias zyx = reverseArgs!(foo); assert(zyx(5, 4) == foo(4, 5)); } /// @safe unittest { alias gt = reverseArgs!(binaryFun!("a < b")); assert(gt(2, 1) && !gt(1, 1)); int x = 42; bool xyz(int a, int b) { return a * x < b / x; } auto foo = &xyz; foo(4, 5); alias zyx = reverseArgs!(foo); assert(zyx(5, 4) == foo(4, 5)); } /// @safe unittest { int abc(int a, int b, int c) { return a * b + c; } alias cba = reverseArgs!abc; assert(abc(91, 17, 32) == cba(32, 17, 91)); } /// @safe unittest { int a(int a) { return a * 2; } alias _a = reverseArgs!a; assert(a(2) == _a(2)); } /// @safe unittest { int b() { return 4; } alias _b = reverseArgs!b; assert(b() == _b()); } /** Negates predicate `pred`. Params: pred = A string or a callable Returns: A function which calls `pred` and returns the logical negation of its return value. */ template not(alias pred) { auto not(T...)(auto ref T args) { static if (is(typeof(!pred(args)))) return !pred(args); else static if (T.length == 1) return !unaryFun!pred(args); else static if (T.length == 2) return !binaryFun!pred(args); else static assert(0); } } /// @safe unittest { import std.algorithm.searching : find; import std.uni : isWhite; string a = " Hello, world!"; assert(find!(not!isWhite)(a) == "Hello, world!"); } @safe unittest { assert(not!"a != 5"(5)); assert(not!"a != b"(5, 5)); assert(not!(() => false)()); assert(not!(a => a != 5)(5)); assert(not!((a, b) => a != b)(5, 5)); assert(not!((a, b, c) => a * b * c != 125 )(5, 5, 5)); } /** $(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially applies) $(D_PARAM fun) by tying its first argument to $(D_PARAM arg). Params: fun = A callable arg = The first argument to apply to `fun` Returns: A new function which calls `fun` with `arg` plus the passed parameters. */ template partial(alias fun, alias arg) { import std.traits : isCallable; // Check whether fun is a user defined type which implements opCall or a template. // As opCall itself can be templated, std.traits.isCallable does not work here. enum isSomeFunctor = (is(typeof(fun) == struct) || is(typeof(fun) == class)) && __traits(hasMember, fun, "opCall"); static if (isSomeFunctor || __traits(isTemplate, fun)) { auto partial(Ts...)(Ts args2) { static if (is(typeof(fun(arg, args2)))) { return fun(arg, args2); } else { static string errormsg() { string msg = "Cannot call '" ~ fun.stringof ~ "' with arguments " ~ "(" ~ arg.stringof; foreach (T; Ts) msg ~= ", " ~ T.stringof; msg ~= ")."; return msg; } static assert(0, errormsg()); } } } else static if (!isCallable!fun) { static assert(false, "Cannot apply partial to a non-callable '" ~ fun.stringof ~ "'."); } else { import std.meta : Filter; static if (__traits(compiles, __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun)))) alias overloads = __traits(getOverloads, __traits(parent, fun), __traits(identifier, fun)); else alias overloads = AliasSeq!(fun); enum isCallableWithArg(alias fun) = Parameters!fun.length > 0 && is(typeof(arg) : Parameters!fun[0]); alias candidates = Filter!(isCallableWithArg, overloads); static if (overloads.length == 1 && Parameters!fun.length == 0) { static assert(0, "Cannot partially apply '" ~ fun.stringof ~ "'." ~ "'" ~ fun.stringof ~ "' has 0 arguments."); } else static if (candidates.length == 0) { import std.meta : NoDuplicates, staticMap; enum hasParameters(alias fun) = Parameters!fun.length > 0; alias firstParameter(alias fun) = Parameters!fun[0]; alias firstParameters = NoDuplicates!( staticMap!(firstParameter, Filter!(hasParameters, overloads))); string errorMsg() { string msg = "Argument mismatch for '" ~ fun.stringof ~ "': expected " ~ firstParameters[0].stringof; static foreach (firstParam; firstParameters[1 .. $]) msg ~= " or " ~ firstParam.stringof; msg ~= ", but got " ~ typeof(arg).stringof ~ "."; return msg; } static assert(0, errorMsg()); } else { import std.traits : ReturnType; static foreach (candidate; candidates) ReturnType!candidate partial(Parameters!candidate[1..$] args2) { return candidate(arg, args2); } } } } /// @safe unittest { int fun(int a, int b) { return a + b; } alias fun5 = partial!(fun, 5); assert(fun5(6) == 11); // Note that in most cases you'd use an alias instead of a value // assignment. Using an alias allows you to partially evaluate template // functions without committing to a particular type of the function. } // https://issues.dlang.org/show_bug.cgi?id=21457 /// @safe unittest { // Overloads are resolved when the partially applied function is called // with the remaining arguments. struct S { static char fun(int i, string s) { return s[i]; } static int fun(int a, int b) { return a * b; } } alias fun3 = partial!(S.fun, 3); assert(fun3("hello") == 'l'); assert(fun3(10) == 30); } // tests for partially evaluating callables @safe unittest { static int f1(int a, int b) { return a + b; } assert(partial!(f1, 5)(6) == 11); int f2(int a, int b) { return a + b; } int x = 5; assert(partial!(f2, x)(6) == 11); x = 7; assert(partial!(f2, x)(6) == 13); static assert(partial!(f2, 5)(6) == 11); auto dg = &f2; auto f3 = &partial!(dg, x); assert(f3(6) == 13); static int funOneArg(int a) { return a; } assert(partial!(funOneArg, 1)() == 1); static int funThreeArgs(int a, int b, int c) { return a + b + c; } alias funThreeArgs1 = partial!(funThreeArgs, 1); assert(funThreeArgs1(2, 3) == 6); static assert(!is(typeof(funThreeArgs1(2)))); enum xe = 5; alias fe = partial!(f2, xe); static assert(fe(6) == 11); } // tests for partially evaluating templated/overloaded callables @safe unittest { static auto add(A, B)(A x, B y) { return x + y; } alias add5 = partial!(add, 5); assert(add5(6) == 11); static assert(!is(typeof(add5()))); static assert(!is(typeof(add5(6, 7)))); // taking address of templated partial evaluation needs explicit type auto dg = &add5!(int); assert(dg(6) == 11); int x = 5; alias addX = partial!(add, x); assert(addX(6) == 11); static struct Callable { static string opCall(string a, string b) { return a ~ b; } int opCall(int a, int b) { return a * b; } double opCall(double a, double b) { return a + b; } } Callable callable; assert(partial!(Callable, "5")("6") == "56"); assert(partial!(callable, 5)(6) == 30); assert(partial!(callable, 7.0)(3.0) == 7.0 + 3.0); static struct TCallable { auto opCall(A, B)(A a, B b) { return a + b; } } TCallable tcallable; assert(partial!(tcallable, 5)(6) == 11); static assert(!is(typeof(partial!(tcallable, "5")(6)))); static struct NonCallable{} static assert(!__traits(compiles, partial!(NonCallable, 5)), "Partial should not work on non-callable structs."); static assert(!__traits(compiles, partial!(NonCallable.init, 5)), "Partial should not work on instances of non-callable structs."); static A funOneArg(A)(A a) { return a; } alias funOneArg1 = partial!(funOneArg, 1); assert(funOneArg1() == 1); static auto funThreeArgs(A, B, C)(A a, B b, C c) { return a + b + c; } alias funThreeArgs1 = partial!(funThreeArgs, 1); assert(funThreeArgs1(2, 3) == 6); static assert(!is(typeof(funThreeArgs1(1)))); auto dg2 = &funOneArg1!(); assert(dg2() == 1); } // Fix https://issues.dlang.org/show_bug.cgi?id=15732 @safe unittest { // Test whether it works with functions. auto partialFunction(){ auto fullFunction = (float a, float b, float c) => a + b / c; alias apply1 = partial!(fullFunction, 1); return &apply1; } auto result = partialFunction()(2, 4); assert(result == 1.5f); // And with delegates. auto partialDelegate(float c){ auto fullDelegate = (float a, float b) => a + b / c; alias apply1 = partial!(fullDelegate, 1); return &apply1; } auto result2 = partialDelegate(4)(2); assert(result2 == 1.5f); } /** Takes a function of (potentially) many arguments, and returns a function taking one argument and returns a callable taking the rest. f(x, y) == curry(f)(x)(y) Params: F = a function taking at least one argument t = a callable object whose opCall takes at least 1 object Returns: A single parameter callable object */ template curry(alias F) if (isCallable!F && Parameters!F.length) { //inspired from the implementation from Artur Skawina here: //https://forum.dlang.org/post/mailman.1626.1340110492.24740.digitalmars-d@puremagic.com //This implementation stores a copy of all filled in arguments with each curried result //this way, the curried functions are independent and don't share any references //eg: auto fc = curry!f; auto fc1 = fc(1); auto fc2 = fc(2); fc1(3) != fc2(3) struct CurryImpl(size_t N) { alias FParams = Parameters!F; FParams[0 .. N] storedArguments; static if (N > 0) { this(U : FParams[N - 1])(ref CurryImpl!(N - 1) prev, ref U arg) { storedArguments[0 .. N - 1] = prev.storedArguments[]; storedArguments[N-1] = arg; } } auto opCall(U : FParams[N])(auto ref U arg) return scope { static if (N == FParams.length - 1) { return F(storedArguments, arg); } else { return CurryImpl!(N + 1)(this, arg); } } } auto curry() { CurryImpl!0 res; return res; // return CurryImpl!0.init segfaults for delegates on Windows } } /// pure @safe @nogc nothrow unittest { int f(int x, int y, int z) { return x + y + z; } auto cf = curry!f; auto cf1 = cf(1); auto cf2 = cf(2); assert(cf1(2)(3) == f(1, 2, 3)); assert(cf2(2)(3) == f(2, 2, 3)); } ///ditto auto curry(T)(T t) if (isCallable!T && Parameters!T.length) { static auto fun(ref T inst, ref Parameters!T args) { return inst(args); } return curry!fun()(t); } /// pure @safe @nogc nothrow unittest { //works with callable structs too struct S { int w; int opCall(int x, int y, int z) { return w + x + y + z; } } S s; s.w = 5; auto cs = curry(s); auto cs1 = cs(1); auto cs2 = cs(2); assert(cs1(2)(3) == s(1, 2, 3)); assert(cs1(2)(3) == (1 + 2 + 3 + 5)); assert(cs2(2)(3) ==s(2, 2, 3)); } @safe pure @nogc nothrow unittest { //currying a single argument function does nothing int pork(int a){ return a*2;} auto curryPork = curry!pork; assert(curryPork(0) == pork(0)); assert(curryPork(1) == pork(1)); assert(curryPork(-1) == pork(-1)); assert(curryPork(1000) == pork(1000)); //test 2 argument function double mixedVeggies(double a, int b, bool) { return a + b; } auto mixedCurry = curry!mixedVeggies; assert(mixedCurry(10)(20)(false) == mixedVeggies(10, 20, false)); assert(mixedCurry(100)(200)(true) == mixedVeggies(100, 200, true)); // struct with opCall struct S { double opCall(int x, double y, short z) const pure nothrow @nogc { return x*y*z; } } S s; auto curriedStruct = curry(s); assert(curriedStruct(1)(2)(short(3)) == s(1, 2, short(3))); assert(curriedStruct(300)(20)(short(10)) == s(300, 20, short(10))); } pure @safe nothrow unittest { auto cfl = curry!((double a, int b) => a + b); assert(cfl(13)(2) == 15); int c = 42; auto cdg = curry!((double a, int b) => a + b + c); assert(cdg(13)(2) == 57); static class C { int opCall(int mult, int add) pure @safe nothrow @nogc scope { return mult * 42 + add; } } scope C ci = new C(); scope cc = curry(ci); assert(cc(2)(4) == ci(2, 4)); } // Disallows callables without parameters pure @safe @nogc nothrow unittest { static void noargs() {} static assert(!__traits(compiles, curry!noargs())); static struct NoArgs { void opCall() {} } static assert(!__traits(compiles, curry(NoArgs.init))); } private template Iota(size_t n) { static if (n == 0) alias Iota = AliasSeq!(); else alias Iota = AliasSeq!(Iota!(n - 1), n - 1); } /** Takes multiple functions and adjoins them together. Params: F = the call-able(s) to adjoin Returns: A new function which returns a $(REF Tuple, std,typecons). Each of the elements of the tuple will be the return values of `F`. Note: In the special case where only a single function is provided ($(D F.length == 1)), adjoin simply aliases to the single passed function (`F[0]`). */ template adjoin(F...) if (F.length >= 1) { static if (F.length == 1) alias adjoin = F[0]; else auto adjoin(V...)(auto ref V a) { import std.typecons : tuple; import std.meta : staticMap; auto resultElement(size_t i)() { return F[i](a); } return tuple(staticMap!(resultElement, Iota!(F.length))); } } /// @safe unittest { import std.typecons : Tuple; static bool f1(int a) { return a != 0; } static int f2(int a) { return a / 2; } auto x = adjoin!(f1, f2)(5); assert(is(typeof(x) == Tuple!(bool, int))); assert(x[0] == true && x[1] == 2); } @safe unittest { import std.typecons : Tuple; static bool F1(int a) { return a != 0; } auto x1 = adjoin!(F1)(5); static int F2(int a) { return a / 2; } auto x2 = adjoin!(F1, F2)(5); assert(is(typeof(x2) == Tuple!(bool, int))); assert(x2[0] && x2[1] == 2); auto x3 = adjoin!(F1, F2, F2)(5); assert(is(typeof(x3) == Tuple!(bool, int, int))); assert(x3[0] && x3[1] == 2 && x3[2] == 2); bool F4(int a) { return a != x1; } alias eff4 = adjoin!(F4); static struct S { bool delegate(int) @safe store; int fun() { return 42 + store(5); } } S s; s.store = (int a) { return eff4(a); }; auto x4 = s.fun(); assert(x4 == 43); } @safe unittest { import std.meta : staticMap; import std.typecons : Tuple, tuple; alias funs = staticMap!(unaryFun, "a", "a * 2", "a * 3", "a * a", "-a"); alias afun = adjoin!funs; assert(afun(5) == tuple(5, 10, 15, 25, -5)); static class C{} alias IC = immutable(C); IC foo(){return typeof(return).init;} Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)(); static struct S{int* p;} alias IS = immutable(S); IS bar(){return typeof(return).init;} enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)(); } // https://issues.dlang.org/show_bug.cgi?id=21347 @safe @betterC unittest { alias f = (int n) => n + 1; alias g = (int n) => n + 2; alias h = (int n) => n + 3; alias i = (int n) => n + 4; auto result = adjoin!(f, g, h, i)(0); assert(result[0] == 1); assert(result[1] == 2); assert(result[2] == 3); assert(result[3] == 4); } /** Composes passed-in functions $(D fun[0], fun[1], ...). Params: fun = the call-able(s) or `string`(s) to compose into one function Returns: A new function `f(x)` that in turn returns `fun[0](fun[1](...(x)))...`. See_Also: $(LREF pipe) */ template compose(fun...) if (fun.length > 0) { static if (fun.length == 1) { alias compose = unaryFun!(fun[0]); } else { alias fun0 = unaryFun!(fun[0]); alias rest = compose!(fun[1 .. $]); auto compose(Args...)(Args args) { return fun0(rest(args)); } } } /// @safe unittest { import std.algorithm.comparison : equal; import std.algorithm.iteration : map; import std.array : split; import std.conv : to; // First split a string in whitespace-separated tokens and then // convert each token into an integer assert(compose!(map!(to!(int)), split)("1 2 3").equal([1, 2, 3])); } // https://issues.dlang.org/show_bug.cgi?id=6484 @safe unittest { int f(int a) { return a; } int g(int a) { return a; } int h(int a,int b,int c) { return a * b * c; } alias F = compose!(f,g,h); assert(F(1,2,3) == f(g(h(1,2,3)))); } /** Pipes functions in sequence. Offers the same functionality as $(D compose), but with functions specified in reverse order. This may lead to more readable code in some situation because the order of execution is the same as lexical order. Params: fun = the call-able(s) or `string`(s) to compose into one function Returns: A new function `f(x)` that in turn returns `fun[$-1](...fun[1](fun[0](x)))...`. Example: ---- // Read an entire text file, split the resulting string in // whitespace-separated tokens, and then convert each token into an // integer int[] a = pipe!(readText, split, map!(to!(int)))("file.txt"); ---- See_Also: $(LREF compose) */ alias pipe(fun...) = compose!(Reverse!(fun)); /// @safe unittest { import std.conv : to; string foo(int a) { return to!(string)(a); } int bar(string a) { return to!(int)(a) + 1; } double baz(int a) { return a + 0.5; } assert(compose!(baz, bar, foo)(1) == 2.5); assert(pipe!(foo, bar, baz)(1) == 2.5); assert(compose!(baz, `to!(int)(a) + 1`, foo)(1) == 2.5); assert(compose!(baz, bar)("1"[]) == 2.5); assert(compose!(baz, bar)("1") == 2.5); assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5); } /** * $(LINK2 https://en.wikipedia.org/wiki/Memoization, Memoizes) a function so as * to avoid repeated computation. The memoization structure is a hash table keyed by a * tuple of the function's arguments. There is a speed gain if the * function is repeatedly called with the same arguments and is more * expensive than a hash table lookup. For more information on memoization, refer to $(HTTP docs.google.com/viewer?url=http%3A%2F%2Fhop.perl.plover.com%2Fbook%2Fpdf%2F03CachingAndMemoization.pdf, this book chapter). Example: ---- double transmogrify(int a, string b) { ... expensive computation ... } alias fastTransmogrify = memoize!transmogrify; unittest { auto slow = transmogrify(2, "hello"); auto fast = fastTransmogrify(2, "hello"); assert(slow == fast); } ---- Params: fun = the call-able to memozie maxSize = The maximum size of the GC buffer to hold the return values Returns: A new function which calls `fun` and caches its return values. Note: Technically the memoized function should be pure because `memoize` assumes it will always return the same result for a given tuple of arguments. However, `memoize` does not enforce that because sometimes it is useful to memoize an impure function, too. */ template memoize(alias fun) { import std.traits : ReturnType; // https://issues.dlang.org/show_bug.cgi?id=13580 // alias Args = Parameters!fun; ReturnType!fun memoize(Parameters!fun args) { alias Args = Parameters!fun; import std.typecons : Tuple; import std.traits : Unqual; static Unqual!(ReturnType!fun)[Tuple!Args] memo; auto t = Tuple!Args(args); if (auto p = t in memo) return *p; auto r = fun(args); memo[t] = r; return r; } } /// ditto template memoize(alias fun, uint maxSize) { import std.traits : ReturnType; // https://issues.dlang.org/show_bug.cgi?id=13580 // alias Args = Parameters!fun; ReturnType!fun memoize(Parameters!fun args) { import std.meta : staticMap; import std.traits : hasIndirections, Unqual; import std.typecons : tuple; static struct Value { staticMap!(Unqual, Parameters!fun) args; Unqual!(ReturnType!fun) res; } static Value[] memo; static size_t[] initialized; if (!memo.length) { import core.memory : GC; // Ensure no allocation overflows static assert(maxSize < size_t.max / Value.sizeof); static assert(maxSize < size_t.max - (8 * size_t.sizeof - 1)); enum attr = GC.BlkAttr.NO_INTERIOR | (hasIndirections!Value ? 0 : GC.BlkAttr.NO_SCAN); memo = (cast(Value*) GC.malloc(Value.sizeof * maxSize, attr))[0 .. maxSize]; enum nwords = (maxSize + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); initialized = (cast(size_t*) GC.calloc(nwords * size_t.sizeof, attr | GC.BlkAttr.NO_SCAN))[0 .. nwords]; } import core.bitop : bt, bts; import core.lifetime : emplace; size_t hash; foreach (ref arg; args) hash = hashOf(arg, hash); // cuckoo hashing immutable idx1 = hash % maxSize; if (!bt(initialized.ptr, idx1)) { emplace(&memo[idx1], args, fun(args)); // only set to initialized after setting args and value // https://issues.dlang.org/show_bug.cgi?id=14025 bts(initialized.ptr, idx1); return memo[idx1].res; } else if (memo[idx1].args == args) return memo[idx1].res; // FNV prime immutable idx2 = (hash * 16_777_619) % maxSize; if (!bt(initialized.ptr, idx2)) { emplace(&memo[idx2], memo[idx1]); bts(initialized.ptr, idx2); } else if (memo[idx2].args == args) return memo[idx2].res; else if (idx1 != idx2) memo[idx2] = memo[idx1]; memo[idx1] = Value(args, fun(args)); return memo[idx1].res; } } /** * To _memoize a recursive function, simply insert the memoized call in lieu of the plain recursive call. * For example, to transform the exponential-time Fibonacci implementation into a linear-time computation: */ @safe nothrow unittest { ulong fib(ulong n) @safe nothrow { return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1); } assert(fib(10) == 55); } /** * To improve the speed of the factorial function, */ @safe unittest { ulong fact(ulong n) @safe { return n < 2 ? 1 : n * memoize!fact(n - 1); } assert(fact(10) == 3628800); } /** * This memoizes all values of `fact` up to the largest argument. To only cache the final * result, move `memoize` outside the function as shown below. */ @safe unittest { ulong factImpl(ulong n) @safe { return n < 2 ? 1 : n * factImpl(n - 1); } alias fact = memoize!factImpl; assert(fact(10) == 3628800); } /** * When the `maxSize` parameter is specified, memoize will used * a fixed size hash table to limit the number of cached entries. */ @system unittest // not @safe due to memoize { ulong fact(ulong n) { // Memoize no more than 8 values return n < 2 ? 1 : n * memoize!(fact, 8)(n - 1); } assert(fact(8) == 40320); // using more entries than maxSize will overwrite existing entries assert(fact(10) == 3628800); } @system unittest // not @safe due to memoize { import core.math : sqrt; alias msqrt = memoize!(function double(double x) { return sqrt(x); }); auto y = msqrt(2.0); assert(y == msqrt(2.0)); y = msqrt(4.0); assert(y == sqrt(4.0)); // alias mrgb2cmyk = memoize!rgb2cmyk; // auto z = mrgb2cmyk([43, 56, 76]); // assert(z == mrgb2cmyk([43, 56, 76])); //alias mfib = memoize!fib; static ulong fib(ulong n) @safe { alias mfib = memoize!fib; return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1); } auto z = fib(10); assert(z == 89); static ulong fact(ulong n) @safe { alias mfact = memoize!fact; return n < 2 ? 1 : n * mfact(n - 1); } assert(fact(10) == 3628800); // https://issues.dlang.org/show_bug.cgi?id=12568 static uint len2(const string s) { // Error alias mLen2 = memoize!len2; if (s.length == 0) return 0; else return 1 + mLen2(s[1 .. $]); } int _func(int x) @safe { return 1; } alias func = memoize!(_func, 10); assert(func(int.init) == 1); assert(func(int.init) == 1); } // https://issues.dlang.org/show_bug.cgi?id=16079 // memoize should work with arrays @system unittest // not @safe with -dip1000 due to memoize { int executed = 0; T median(T)(const T[] nums) { import std.algorithm.sorting : sort; executed++; auto arr = nums.dup; arr.sort(); if (arr.length % 2) return arr[$ / 2]; else return (arr[$ / 2 - 1] + arr[$ / 2]) / 2; } alias fastMedian = memoize!(median!int); assert(fastMedian([7, 5, 3]) == 5); assert(fastMedian([7, 5, 3]) == 5); assert(executed == 1); } // https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with structs @safe unittest { int executed = 0; T pickFirst(T)(T first) { executed++; return first; } struct Foo { int k; } Foo A = Foo(3); alias first = memoize!(pickFirst!Foo); assert(first(Foo(3)) == A); assert(first(Foo(3)) == A); assert(executed == 1); } // https://issues.dlang.org/show_bug.cgi?id=20439 memoize should work with void opAssign @safe unittest { static struct S { void opAssign(S) {} } assert(memoize!(() => S()) == S()); } // https://issues.dlang.org/show_bug.cgi?id=16079: memoize should work with classes @system unittest // not @safe with -dip1000 due to memoize { int executed = 0; T pickFirst(T)(T first) { executed++; return first; } class Bar { size_t k; this(size_t k) { this.k = k; } override size_t toHash() { return k; } override bool opEquals(Object o) { auto b = cast(Bar) o; return b && k == b.k; } } alias firstClass = memoize!(pickFirst!Bar); assert(firstClass(new Bar(3)).k == 3); assert(firstClass(new Bar(3)).k == 3); assert(executed == 1); } // https://issues.dlang.org/show_bug.cgi?id=20302 @system unittest { version (none) // TODO change `none` to `all` and fix remaining limitations struct S { const int len; } else struct S { int len; } static string fun000( string str, S s) { return str[0 .. s.len] ~ "123"; } static string fun001( string str, const S s) { return str[0 .. s.len] ~ "123"; } static string fun010(const string str, S s) { return str[0 .. s.len] ~ "123"; } static string fun011(const string str, const S s) { return str[0 .. s.len] ~ "123"; } static const(string) fun100( string str, S s) { return str[0 .. s.len] ~ "123"; } static const(string) fun101( string str, const S s) { return str[0 .. s.len] ~ "123"; } static const(string) fun110(const string str, S s) { return str[0 .. s.len] ~ "123"; } static const(string) fun111(const string str, const S s) { return str[0 .. s.len] ~ "123"; } static foreach (fun; AliasSeq!(fun000, fun001, fun010, fun011, fun100, fun101, fun110, fun111)) {{ alias mfun = memoize!fun; assert(mfun("abcdefgh", S(3)) == "abc123"); alias mfun2 = memoize!(fun, 42); assert(mfun2("asd", S(3)) == "asd123"); }} } private struct DelegateFaker(F) { import std.typecons : FuncInfo, MemberFunctionGenerator; // for @safe static F castToF(THIS)(THIS x) @trusted { return cast(F) x; } /* * What all the stuff below does is this: *-------------------- * struct DelegateFaker(F) { * extern(linkage) * [ref] ReturnType!F doIt(Parameters!F args) [@attributes] * { * auto fp = cast(F) &this; * return fp(args); * } * } *-------------------- */ // We will use MemberFunctionGenerator in std.typecons. This is a policy // configuration for generating the doIt(). template GeneratingPolicy() { // Inform the genereator that we only have type information. enum WITHOUT_SYMBOL = true; // Generate the function body of doIt(). template generateFunctionBody(unused...) { enum generateFunctionBody = // [ref] ReturnType doIt(Parameters args) @attributes q{ // When this function gets called, the this pointer isn't // really a this pointer (no instance even really exists), but // a function pointer that points to the function to be called. // Cast it to the correct type and call it. auto fp = castToF(&this); return fp(args); }; } } // Type information used by the generated code. alias FuncInfo_doIt = FuncInfo!(F); // Generate the member function doIt(). mixin( MemberFunctionGenerator!(GeneratingPolicy!()) .generateFunction!("FuncInfo_doIt", "doIt", F) ); } /** * Convert a callable to a delegate with the same parameter list and * return type, avoiding heap allocations and use of auxiliary storage. * * Params: * fp = a function pointer or an aggregate type with `opCall` defined. * Returns: * A delegate with the context pointer pointing to nothing. * * Example: * ---- * void doStuff() { * writeln("Hello, world."); * } * * void runDelegate(void delegate() myDelegate) { * myDelegate(); * } * * auto delegateToPass = toDelegate(&doStuff); * runDelegate(delegateToPass); // Calls doStuff, prints "Hello, world." * ---- * * BUGS: * $(UL * $(LI Does not work with `@safe` functions.) * $(LI Ignores C-style / D-style variadic arguments.) * ) */ auto toDelegate(F)(auto ref F fp) if (isCallable!(F)) { static if (is(F == delegate)) { return fp; } else static if (is(typeof(&F.opCall) == delegate) || (is(typeof(&F.opCall) V : V*) && is(V == function))) { return toDelegate(&fp.opCall); } else { alias DelType = typeof(&(new DelegateFaker!(F)).doIt); static struct DelegateFields { union { DelType del; //pragma(msg, typeof(del)); struct { void* contextPtr; void* funcPtr; } } } // fp is stored in the returned delegate's context pointer. // The returned delegate's function pointer points to // DelegateFaker.doIt. DelegateFields df; df.contextPtr = cast(void*) fp; DelegateFaker!(F) dummy; auto dummyDel = &dummy.doIt; df.funcPtr = dummyDel.funcptr; return df.del; } } /// @system unittest { static int inc(ref uint num) { num++; return 8675309; } uint myNum = 0; auto incMyNumDel = toDelegate(&inc); auto returnVal = incMyNumDel(myNum); assert(myNum == 1); } @system unittest // not @safe due to toDelegate { static int inc(ref uint num) { num++; return 8675309; } uint myNum = 0; auto incMyNumDel = toDelegate(&inc); int delegate(ref uint) dg = incMyNumDel; auto returnVal = incMyNumDel(myNum); assert(myNum == 1); interface I { int opCall(); } class C: I { int opCall() { inc(myNum); return myNum;} } auto c = new C; auto i = cast(I) c; auto getvalc = toDelegate(c); assert(getvalc() == 2); auto getvali = toDelegate(i); assert(getvali() == 3); struct S1 { int opCall() { inc(myNum); return myNum; } } static assert(!is(typeof(&s1.opCall) == delegate)); S1 s1; auto getvals1 = toDelegate(s1); assert(getvals1() == 4); struct S2 { static int opCall() { return 123456; } } static assert(!is(typeof(&S2.opCall) == delegate)); S2 s2; auto getvals2 =&S2.opCall; assert(getvals2() == 123456); /* test for attributes */ { static int refvar = 0xDeadFace; static ref int func_ref() { return refvar; } static int func_pure() pure { return 1; } static int func_nothrow() nothrow { return 2; } static int func_property() @property { return 3; } static int func_safe() @safe { return 4; } static int func_trusted() @trusted { return 5; } static int func_system() @system { return 6; } static int func_pure_nothrow() pure nothrow { return 7; } static int func_pure_nothrow_safe() pure nothrow @safe { return 8; } auto dg_ref = toDelegate(&func_ref); int delegate() pure dg_pure = toDelegate(&func_pure); int delegate() nothrow dg_nothrow = toDelegate(&func_nothrow); int delegate() @property dg_property = toDelegate(&func_property); int delegate() @safe dg_safe = toDelegate(&func_safe); int delegate() @trusted dg_trusted = toDelegate(&func_trusted); int delegate() @system dg_system = toDelegate(&func_system); int delegate() pure nothrow dg_pure_nothrow = toDelegate(&func_pure_nothrow); int delegate() @safe pure nothrow dg_pure_nothrow_safe = toDelegate(&func_pure_nothrow_safe); //static assert(is(typeof(dg_ref) == ref int delegate())); // [BUG@DMD] assert(dg_ref() == refvar); assert(dg_pure() == 1); assert(dg_nothrow() == 2); assert(dg_property() == 3); assert(dg_safe() == 4); assert(dg_trusted() == 5); assert(dg_system() == 6); assert(dg_pure_nothrow() == 7); assert(dg_pure_nothrow_safe() == 8); } /* test for linkage */ { struct S { extern(C) static void xtrnC() {} extern(D) static void xtrnD() {} } auto dg_xtrnC = toDelegate(&S.xtrnC); auto dg_xtrnD = toDelegate(&S.xtrnD); static assert(! is(typeof(dg_xtrnC) == typeof(dg_xtrnD))); } } /** * Passes the fields of a struct as arguments to a function. * * Can be used with a $(LINK2 https://dlang.org/spec/expression.html#function_literals, * function literal) to give temporary names to the fields of a struct or * tuple. * * Params: * fun = Callable that the struct's fields will be passed to. * * Returns: * A function that accepts a single struct as an argument and passes its * fields to `fun` when called. */ template bind(alias fun) { /** * Params: * args = The struct or tuple whose fields will be used as arguments. * * Returns: `fun(args.tupleof)` */ auto ref bind(T)(auto ref T args) if (is(T == struct)) { import std.meta : Map = staticMap; import core.lifetime : move; // Forwards the i'th member of `args` // Needed because core.lifetime.forward doesn't work on struct members template forwardArg(size_t i) { static if (__traits(isRef, args) || !is(typeof(move(args.tupleof[i])))) enum forwardArg = "args.tupleof[" ~ toCtString!i ~ "], "; else enum forwardArg = "move(args.tupleof[" ~ toCtString!i ~ "]), "; } static if (args.tupleof.length == 0) enum argList = ""; else alias argList = Map!(forwardArg, Iota!(args.tupleof.length)); return mixin("fun(", argList, ")"); } } /// Giving names to tuple elements @safe unittest { import std.typecons : tuple; auto name = tuple("John", "Doe"); string full = name.bind!((first, last) => first ~ " " ~ last); assert(full == "John Doe"); } /// Passing struct fields to a function @safe unittest { import std.algorithm.comparison : min, max; struct Pair { int a; int b; } auto p = Pair(123, 456); assert(p.bind!min == 123); // min(p.a, p.b) assert(p.bind!max == 456); // max(p.a, p.b) } /// In a range pipeline @safe unittest { import std.algorithm.iteration : map, filter; import std.algorithm.comparison : equal; import std.typecons : tuple; auto ages = [ tuple("Alice", 35), tuple("Bob", 64), tuple("Carol", 21), tuple("David", 39), tuple("Eve", 50) ]; auto overForty = ages .filter!(bind!((name, age) => age > 40)) .map!(bind!((name, age) => name)); assert(overForty.equal(["Bob", "Eve"])); } // Zero arguments @safe unittest { struct Empty {} assert(Empty().bind!(() => 123) == 123); } // Non-copyable arguments @safe unittest { import std.typecons : tuple; static struct NoCopy { int n; @disable this(this); } static struct Pair { NoCopy a, b; } static auto fun(NoCopy a, NoCopy b) { return tuple(a.n, b.n); } auto expected = fun(NoCopy(1), NoCopy(2)); assert(Pair(NoCopy(1), NoCopy(2)).bind!fun == expected); } // ref arguments @safe unittest { import std.typecons : tuple; auto t = tuple(123, 456); t.bind!((ref int a, int b) { a = 789; b = 1011; }); assert(t[0] == 789); assert(t[1] == 456); } // auto ref arguments @safe unittest { import std.typecons : tuple; auto t = tuple(123); t.bind!((auto ref x) { static assert(__traits(isRef, x)); }); tuple(123).bind!((auto ref x) { static assert(!__traits(isRef, x)); }); }