1 /** 2 * Functor primitives. 3 * 4 * Functors are objects which are callable. Unlike function pointers 5 * or delegates, functors may embed state, and don't require a context 6 * pointer. 7 * 8 * Function pointers and delegates are functors; their state contains 9 * a pointer to the implementation and context. 10 * 11 * https://forum.dlang.org/post/qnigarkuxxnqwdernhzv@forum.dlang.org 12 * 13 * License: 14 * This Source Code Form is subject to the terms of 15 * the Mozilla Public License, v. 2.0. If a copy of 16 * the MPL was not distributed with this file, You 17 * can obtain one at http://mozilla.org/MPL/2.0/. 18 * 19 * Authors: 20 * Vladimir Panteleev <ae@cy.md> 21 */ 22 23 module ae.utils.functor.primitives; 24 25 import std.functional : forward; 26 27 // Avoid https://issues.dlang.org/show_bug.cgi?id=23901, which for 28 // some reason manifests only with the `std.algorithm.mutation` 29 // versions of `move` in recent D versions. 30 static if (is(typeof({ import core.lifetime : move; }))) 31 import core.lifetime : move; 32 else 33 import std.algorithm.mutation : move; 34 35 /// Constructs a functor with statically-defined behavior (using an 36 /// alias), with optional state. 37 template functor(alias fun, State...) 38 { 39 struct Functor 40 { 41 State state; 42 43 // With https://issues.dlang.org/show_bug.cgi?id=9608, we 44 // might be able to introspect `fun` and generate accessors 45 // for `state` based on `fun`'s parameter names. 46 47 static if (state.length) 48 private this(State state) 49 { 50 static foreach (i; 0 .. state.length) 51 static if (is(typeof(move(state[i])))) 52 this.state[i] = move(state[i]); 53 else 54 this.state[i] = state[i]; 55 } 56 57 auto ref opCall(this This, Args...)(auto ref Args args) 58 { 59 static if (args.length) 60 return fun(state, forward!args); 61 else 62 return fun(state); 63 } 64 } 65 66 auto functor(State state) 67 { 68 static if (state.length) 69 return Functor(forward!state); 70 else 71 { 72 Functor functor; 73 return functor; 74 } 75 } 76 } 77 78 /// 79 @safe pure @nogc nothrow unittest 80 { 81 auto getFive = functor!(() => 5)(); 82 assert(getFive() == 5); 83 84 auto getValue = functor!(n => n)(5); 85 assert(getValue() == 5); 86 87 // Functor construction is a bit like currying, though mutation of 88 // curried arguments (here, state) is explicitly allowed. 89 90 auto addValue = functor!((n, i) => n + i)(2); 91 assert(addValue(5) == 7); 92 93 auto accumulator = functor!((ref n, i) => n += i)(0); 94 accumulator(2); accumulator(5); 95 assert(accumulator.state[0] == 7); 96 } 97 98 /// 99 @safe pure nothrow unittest 100 { 101 // Regular D closures are still supported. Not @nogc! 102 103 auto n = 2; 104 auto addValue = functor!(i => i + n)(); 105 assert(addValue(5) == 7); 106 107 auto m = 3; 108 auto addMul = functor!(i => addValue(i * m))(); 109 assert(addMul(5) == 17); 110 } 111 112 @safe pure @nogc nothrow unittest 113 { 114 struct NC 115 { 116 @disable this(); 117 @disable this(this); 118 int i; 119 this(int i) @nogc { this.i = i; } 120 } 121 122 auto f = functor!((ref a, ref b) => a.i + b.i)(NC(2), NC(3)); 123 assert(f() == 5); 124 125 static if (__VERSION__ >= 2086) mixin(q{ 126 assert(functor!(ref (ref a) => a)(NC(1))().i == 1); 127 }); 128 } 129 130 @safe pure @nogc nothrow unittest 131 { 132 immutable int i = 2; 133 auto f = functor!((a, b) => a + b)(i); 134 assert(f(3) == 5); 135 } 136 137 /// Constructs a nullary functor which simply returns a value specified at compile-time. 138 /// Like `() => value`, but without the indirect call. 139 auto valueFunctor(alias value)() { return .functor!(() => value)(); } 140 141 /// Constructs a nullary functor which simply returns a value specified at run-time. 142 /// Like `() => value`, but without the closure and indirect call. 143 auto valueFunctor(Value)(Value value) { return functor!(v => v)(value); } 144 145 /// 146 @safe pure @nogc nothrow unittest 147 { 148 assert(valueFunctor(5)() == 5); 149 assert(valueFunctor!5()() == 5); 150 }