1 /**
2  * Functor composition.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.utils.functor.composition;
15 
16 import ae.utils.functor.primitives;
17 
18 import std.functional : forward;
19 import std.meta : allSatisfy;
20 import std.traits : isCallable;
21 
22 /// Check if `f` is a functor, and can participate in functor composition.
23 // Work around https://issues.dlang.org/show_bug.cgi?id=20246
24 // (We assume opCall is always a function or function template.)
25 enum isFunctor(f...) = f.length == 1 && (
26 	isCallable!f || __traits(hasMember, f, "opCall")
27 );
28 
29 unittest
30 {
31 	static assert(isFunctor!(typeof(() => 5)));
32 	int i;
33 	static assert(isFunctor!(typeof(() => i)));
34 	auto getFive = functor!(() => 5)();
35 	static assert(isFunctor!getFive);
36 }
37 
38 /// The ternary operation using functors.
39 template select(Cond, T, F)
40 if (isFunctor!Cond && isFunctor!T && isFunctor!F)
41 {
42 	static auto fun(Args...)(Cond cond, T t, F f, auto ref Args args)
43 	{
44 		return cond()
45 			? t(forward!args)
46 			: f(forward!args);
47 	}
48 
49 	auto select(Cond cond, T t, F f) @nogc
50 	{
51 		return functor!fun(cond, t, f);
52 	}
53 }
54 
55 auto select(T, F)(bool cond, T t, F f) @nogc
56 if (isFunctor!T && isFunctor!F)
57 { return select(cond.valueFunctor, t, f); } /// ditto
58 
59 ///
60 unittest
61 {
62 	assert(select(true , 5.valueFunctor, 7.valueFunctor)() == 5);
63 	assert(select(false, 5.valueFunctor, 7.valueFunctor)() == 7);
64 }
65 
66 /// The chain operation using functors.
67 /// Calls all functors in sequence, returns `void`.
68 /// (Not to be confused with function composition.)
69 template seq(Functors...)
70 if (allSatisfy!(isFunctor, Functors))
71 {
72 	static void fun(Args...)(ref Functors functors, auto ref Args args)
73 	{
74 		/*static*/ foreach (ref functor; functors)
75 			functor(args);
76 	}
77 
78 	auto seq(Functors functors) @nogc
79 	{
80 		return functor!fun(functors);
81 	}
82 }
83 
84 ///
85 unittest
86 {
87 	auto addFive = functor!(p => *p += 5)();
88 	auto addThree = functor!(p => *p += 3)();
89 	auto addEight = seq(addFive, addThree);
90 	int i;
91 	addEight(&i);
92 	assert(i == 8);
93 }