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 }