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 }