The OpenD Programming Language

1 /++
2 Functions that manipulate other functions.
3 This module provides functions for compile time function composition. These
4 functions are helpful when constructing predicates for the algorithms in
5 $(MREF mir, ndslice).
6 $(BOOKTABLE $(H2 Functions),
7 $(TR $(TH Function Name) $(TH Description))
8     $(TR $(TD $(LREF naryFun))
9         $(TD Create a unary, binary or N-nary function from a string. Most often
10         used when defining algorithms on ranges and slices.
11     ))
12     $(TR $(TD $(LREF pipe))
13         $(TD Join a couple of functions into one that executes the original
14         functions one after the other, using one function's result for the next
15         function's argument.
16     ))
17     $(TR $(TD $(LREF not))
18         $(TD Creates a function that negates another.
19     ))
20     $(TR $(TD $(LREF reverseArgs))
21         $(TD Predicate that reverses the order of its arguments.
22     ))
23     $(TR $(TD $(LREF forward))
24         $(TD Forwards function arguments with saving ref-ness.
25     ))
26     $(TR $(TD $(LREF tuple))
27         $(TD Removes $(LREF Ref) shell.
28     ))
29     $(TR $(TD $(LREF unref))
30         $(TD Creates a $(LREF Tuple) structure.
31     ))
32     $(TR $(TD $(LREF __ref))
33         $(TD Creates a $(LREF Ref) structure.
34     ))
35 )
36 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
37 Authors: Ilia Ki, $(HTTP erdani.org, Andrei Alexandrescu (some original code from std.functional))
38 
39 Macros:
40 NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP)
41 +/
42 module mir.functional;
43 
44 private enum isRef(T) = is(T : Ref!T0, T0);
45 
46 import mir.math.common: optmath;
47 
48 public import core.lifetime : forward;
49 
50 @optmath:
51 
52 /++
53 Constructs static array.
54 +/
55 T[N] staticArray(T, size_t N)(return scope T[N] a...) {
56     import std.traits: isDynamicArray;
57     static if (isDynamicArray!T) {  
58         T[N] ret;
59         static foreach(i; 0..a.length) ret[i] = a[i];
60         return ret;
61     }
62     else return a;
63 }
64 
65 @safe version(mir_core_test) unittest
66 {
67     string[2] v = ["AA", "BB"];
68     auto res = staticArray(v);
69     assert(res == v);
70 }
71 
72 /++
73 Simple wrapper that holds a pointer.
74 It is used for as workaround to return multiple auto ref values.
75 +/
76 struct Ref(T)
77     if (!isRef!T)
78 {
79     @optmath:
80 
81     @disable this();
82     ///
83     this(ref T value) @trusted
84     {
85         __ptr = &value;
86     }
87     ///
88     T* __ptr;
89     ///
90     ref inout(T) __value() inout @property { return *__ptr; }
91     ///
92     alias __value this;
93 
94     ///
95     bool opEquals(ref scope const T rhs) const scope
96     {
97         return __value == rhs;
98     }
99 
100     ///
101     bool opEquals(scope const T rhs) const scope
102     {
103         return __value == rhs;
104     }
105 
106     static if (__traits(hasMember, T, "toHash") || __traits(isScalar, T))
107     ///
108     size_t toHash() const
109     {
110         return hashOf(__value);
111     }
112 }
113 
114 /// Creates $(LREF Ref) wrapper.
115 Ref!T _ref(T)(ref T value)
116 {
117     return Ref!T(value);
118 }
119 
120 private mixin template _RefTupleMixin(T...)
121     if (T.length <= 26)
122 {
123     static if (T.length)
124     {
125         enum i = T.length - 1;
126         static if (isRef!(T[i]))
127             mixin(`@optmath @property ref ` ~ cast(char)('a' + i) ~ `() { return *expand[` ~ i.stringof ~ `].__ptr; }` );
128         else
129             mixin(`alias ` ~ cast(char)('a' + i) ~ ` = expand[` ~ i.stringof ~ `];`);
130         mixin ._RefTupleMixin!(T[0 .. $-1]);
131     }
132 }
133 
134 /++
135 Simplified tuple structure. Some fields may be type of $(LREF Ref).
136 Ref stores a pointer to a values.
137 +/
138 struct Tuple(T...)
139 {
140     @optmath:
141     T expand;
142     alias expand this;
143     mixin _RefTupleMixin!T;
144 }
145 
146 deprecated("Use 'Tuple' instead")
147 alias RefTuple = Tuple;
148 
149 /// Removes $(LREF Ref) shell.
150 alias Unref(V : Ref!T, T) = T;
151 /// ditto
152 template Unref(V : Tuple!T, T...)
153 {
154     import std.meta: staticMap;
155     alias Unref = Tuple!(staticMap!(.Unref, T));
156 }
157 
158 /// ditto
159 alias Unref(V) = V;
160 
161 /++
162 Returns: a $(LREF Tuple) structure.
163 +/
164 Tuple!Args tuple(Args...)(auto ref Args args)
165 {
166     return Tuple!Args(args);
167 }
168 
169 deprecated("Use 'tuple' instead")
170 alias refTuple = tuple;
171 
172 /// Removes $(LREF Ref) shell.
173 ref T unref(V : Ref!T, T)(scope return V value)
174 {
175     return *value.__ptr;
176 }
177 
178 /// ditto
179 Unref!(Tuple!T) unref(V : Tuple!T, T...)(V value)
180 {
181     typeof(return) ret;
182     foreach(i, ref elem; ret.expand)
183         elem = unref(value.expand[i]);
184     return ret;
185 }
186 
187 /// ditto
188 ref V unref(V)(scope return ref V value)
189 {
190     return value;
191 }
192 
193 /// ditto
194 V unref(V)(V value)
195 {
196     import std.traits: hasElaborateAssign;
197     static if (hasElaborateAssign!V)
198     {
199         import core.lifetime: move;
200         return move(value);
201     }
202     else
203         return value;
204 }
205 
206 private template autoExpandAndForwardElem(alias value)
207 {
208 
209 }
210 
211 template autoExpandAndForward(alias value)
212     if (is(typeof(value) : Tuple!Types, Types...))
213 {
214 
215     import core.lifetime: move;
216     enum isLazy = __traits(isRef,  value) || __traits(isOut,  value) || __traits(isLazy, value);
217     template autoExpandAndForwardElem(size_t i)
218     {
219         alias T = typeof(value.expand[i]);
220         static if (isRef!T)
221         {
222             ref autoExpandAndForwardElem()
223             {
224                 return *value.expand[i].__ptr;
225             }
226         }
227         else
228         {
229             static if (isLazy)
230                 @property ref autoExpandAndForwardElem(){ pragma(inline, true); return value.expand[i]; }
231             else
232             static if (is(typeof(move(value.expand[i]))))
233                 @property auto autoExpandAndForwardElem(){ pragma(inline, true); return move(value.expand[i]); }
234             else
235                 @property auto autoExpandAndForwardElem(){ pragma(inline, true); return value.expand[i]; }
236         }
237     }
238 
239     import mir.internal.utility: Iota;
240     import std.meta: staticMap;
241     alias autoExpandAndForward = staticMap!(autoExpandAndForwardElem, Iota!(value.expand.length));
242 }
243 
244 version(mir_core_test) unittest
245 {
246     long v;
247     auto tup = tuple(v._ref, 2.3);
248 
249     auto f(ref long a, double b)
250     {
251         assert(b == 2.3);
252         assert(a == v);
253         assert(&a == &v);
254     }
255 
256     f(autoExpandAndForward!tup);
257 }
258 
259 private string joinStrings()(string[] strs)
260 {
261     if (strs.length)
262     {
263         auto ret = strs[0];
264         foreach(s; strs[1 .. $])
265             ret ~= s;
266         return ret;
267     }
268     return null;
269 }
270 
271 private auto copyArg(alias a)()
272 {
273     return a;
274 }
275 
276 /++
277 Takes multiple functions and adjoins them together. The result is a
278 $(LREF Tuple) with one element per passed-in function. Upon
279 invocation, the returned tuple is the adjoined results of all
280 functions.
281 Note: In the special case where only a single function is provided
282 (`F.length == 1`), adjoin simply aliases to the single passed function
283 (`F[0]`).
284 +/
285 template adjoin(fun...) if (fun.length && fun.length <= 26)
286 {
287     static if (fun.length != 1)
288     {
289         import std.meta: staticMap, Filter;
290         static if (Filter!(_needNary, fun).length == 0)
291         {
292             ///
293             @optmath auto adjoin(Args...)(auto ref Args args)
294             {
295                 template _adjoin(size_t i)
296                 {
297                     static if (__traits(compiles, &(fun[i](forward!args))))
298                         enum _adjoin = "Ref!(typeof(fun[" ~ i.stringof ~ "](forward!args)))(fun[" ~ i.stringof ~ "](args)), ";
299                     else
300                         enum _adjoin = "fun[" ~ i.stringof ~ "](args), ";
301                 }
302 
303                 import mir.internal.utility;
304                 mixin("return tuple(" ~ [staticMap!(_adjoin, Iota!(fun.length))].joinStrings ~ ");");
305             }
306         }
307         else alias adjoin = .adjoin!(staticMap!(naryFun, fun));
308     }
309     else alias adjoin = naryFun!(fun[0]);
310 }
311 
312 ///
313 @safe version(mir_core_test) unittest
314 {
315     static bool f1(int a) { return a != 0; }
316     static int f2(int a) { return a / 2; }
317     auto x = adjoin!(f1, f2)(5);
318     assert(is(typeof(x) == Tuple!(bool, int)));
319     assert(x.a == true && x.b == 2);
320 }
321 
322 @safe version(mir_core_test) unittest
323 {
324     alias f = pipe!(adjoin!("a", "a * a"), "a[0]");
325     static assert(is(typeof(f(3)) == int));
326     auto d = 4;
327     static assert(is(typeof(f(d)) == Ref!int));
328 }
329 
330 @safe version(mir_core_test) unittest
331 {
332     static bool F1(int a) { return a != 0; }
333     auto x1 = adjoin!(F1)(5);
334     static int F2(int a) { return a / 2; }
335     auto x2 = adjoin!(F1, F2)(5);
336     assert(is(typeof(x2) == Tuple!(bool, int)));
337     assert(x2.a && x2.b == 2);
338     auto x3 = adjoin!(F1, F2, F2)(5);
339     assert(is(typeof(x3) == Tuple!(bool, int, int)));
340     assert(x3.a && x3.b == 2 && x3.c == 2);
341 
342     bool F4(int a) { return a != x1; }
343     alias eff4 = adjoin!(F4);
344     static struct S
345     {
346         bool delegate(int) @safe store;
347         int fun() { return 42 + store(5); }
348     }
349     S s;
350     s.store = (int a) { return eff4(a); };
351     auto x4 = s.fun();
352     assert(x4 == 43);
353 }
354 
355 //@safe
356 version(mir_core_test) unittest
357 {
358     import std.meta: staticMap;
359     alias funs = staticMap!(naryFun, "a", "a * 2", "a * 3", "a * a", "-a");
360     alias afun = adjoin!funs;
361     int a = 5, b = 5;
362     assert(afun(a) == tuple(Ref!int(a), 10, 15, 25, -5));
363     assert(afun(a) == tuple(Ref!int(b), 10, 15, 25, -5));
364 
365     static class C{}
366     alias IC = immutable(C);
367     IC foo(){return typeof(return).init;}
368     Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)();
369 
370     static struct S{int* p;}
371     alias IS = immutable(S);
372     IS bar(){return typeof(return).init;}
373     enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)();
374 }
375 
376 private template needOpCallAlias(alias fun)
377 {
378     /* Determine whether or not naryFun need to alias to fun or
379      * fun.opCall. Basically, fun is a function object if fun(...) compiles. We
380      * want is(naryFun!fun) (resp., is(naryFun!fun)) to be true if fun is
381      * any function object. There are 4 possible cases:
382      *
383      *  1) fun is the type of a function object with static opCall;
384      *  2) fun is an instance of a function object with static opCall;
385      *  3) fun is the type of a function object with non-static opCall;
386      *  4) fun is an instance of a function object with non-static opCall.
387      *
388      * In case (1), is(naryFun!fun) should compile, but does not if naryFun
389      * aliases itself to fun, because typeof(fun) is an error when fun itself
390      * is a type. So it must be aliased to fun.opCall instead. All other cases
391      * should be aliased to fun directly.
392      */
393     static if (is(typeof(fun.opCall) == function))
394     {
395         import std.traits: Parameters;
396         enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () {
397             return fun(Parameters!fun.init);
398         });
399     }
400     else
401         enum needOpCallAlias = false;
402 }
403 
404 private template _naryAliases(size_t n)
405     if (n <= 26)
406 {
407     static if (n == 0)
408         enum _naryAliases = "";
409     else
410     {
411         enum i = n - 1;
412         enum _naryAliases = _naryAliases!i ~ "alias " ~ cast(char)('a' + i) ~ " = args[" ~ i.stringof ~ "];\n";
413     }
414 }
415 
416 private template stringFun(string fun)
417 {
418     /// Specialization for string lambdas
419     @optmath auto ref stringFun(Args...)(auto ref Args args)
420         if (args.length <= 26 && (Args.length == 0) == (fun.length == 0))
421     {
422         import mir.math.common;
423         static if (fun.length)
424         {
425             mixin(_naryAliases!(Args.length));
426             return mixin(fun);
427         }
428         else
429         {
430             return;
431         }
432     }
433 }
434 
435 /++
436 Aliases itself to a set of functions.
437 
438 Transforms strings representing an expression into a binary function. The
439 strings must use symbol names `a`, `b`, ..., `z`  as the parameters.
440 If `functions[i]` is not a string, `naryFun` aliases itself away to `functions[i]`.
441 +/
442 template naryFun(functions...)
443     if (functions.length >= 1)
444 {
445     static foreach (fun; functions)
446     {
447         static if (is(typeof(fun) : string))
448         {
449             alias naryFun = stringFun!fun;
450         }
451         else static if (needOpCallAlias!fun)
452             alias naryFun = fun.opCall;
453         else
454             alias naryFun = fun;
455     }
456 }
457 
458 ///
459 @safe version(mir_core_test) unittest
460 {
461     // Strings are compiled into functions:
462     alias isEven = naryFun!("(a & 1) == 0");
463     assert(isEven(2) && !isEven(1));
464 }
465 
466 ///
467 @safe version(mir_core_test) unittest
468 {
469     alias less = naryFun!("a < b");
470     assert(less(1, 2) && !less(2, 1));
471     alias greater = naryFun!("a > b");
472     assert(!greater("1", "2") && greater("2", "1"));
473 }
474 
475 /// `naryFun` accepts up to 26 arguments.
476 @safe version(mir_core_test) unittest
477 {
478     assert(naryFun!("a * b + c")(2, 3, 4) == 10);
479 }
480 
481 /// `naryFun` can return by reference.
482 version(mir_core_test) unittest
483 {
484     int a;
485     assert(&naryFun!("a")(a) == &a);
486 }
487 
488 /// `args` parameter tuple
489 version(mir_core_test) unittest
490 {
491     assert(naryFun!("args[0] + args[1]")(2, 3) == 5);
492 }
493 
494 /// Multiple functions
495 @safe pure nothrow @nogc
496 version(mir_core_test) unittest
497 {
498     alias fun = naryFun!(
499         (uint a) => a,
500         (ulong a) => a * 2,
501         a => a * 3,
502     );
503 
504     int a = 10;
505     long b = 10;
506     float c = 10;
507 
508     assert(fun(a) == 10);
509     assert(fun(b) == 20);
510     assert(fun(c) == 30);
511 }
512 
513 @safe version(mir_core_test) unittest
514 {
515     static int f1(int a) { return a + 1; }
516     static assert(is(typeof(naryFun!(f1)(1)) == int));
517     assert(naryFun!(f1)(41) == 42);
518     int f2(int a) { return a + 1; }
519     static assert(is(typeof(naryFun!(f2)(1)) == int));
520     assert(naryFun!(f2)(41) == 42);
521     assert(naryFun!("a + 1")(41) == 42);
522 
523     int num = 41;
524     assert(naryFun!"a + 1"(num) == 42);
525 
526     // Issue 9906
527     struct Seen
528     {
529         static bool opCall(int n) { return true; }
530     }
531     static assert(needOpCallAlias!Seen);
532     static assert(is(typeof(naryFun!Seen(1))));
533     assert(naryFun!Seen(1));
534 
535     Seen s;
536     static assert(!needOpCallAlias!s);
537     static assert(is(typeof(naryFun!s(1))));
538     assert(naryFun!s(1));
539 
540     struct FuncObj
541     {
542         bool opCall(int n) { return true; }
543     }
544     FuncObj fo;
545     static assert(!needOpCallAlias!fo);
546     static assert(is(typeof(naryFun!fo)));
547     assert(naryFun!fo(1));
548 
549     // Function object with non-static opCall can only be called with an
550     // instance, not with merely the type.
551     static assert(!is(typeof(naryFun!FuncObj)));
552 }
553 
554 @safe version(mir_core_test) unittest
555 {
556     static int f1(int a, string b) { return a + 1; }
557     static assert(is(typeof(naryFun!(f1)(1, "2")) == int));
558     assert(naryFun!(f1)(41, "a") == 42);
559     string f2(int a, string b) { return b ~ "2"; }
560     static assert(is(typeof(naryFun!(f2)(1, "1")) == string));
561     assert(naryFun!(f2)(1, "4") == "42");
562     assert(naryFun!("a + b")(41, 1) == 42);
563     //@@BUG
564     //assert(naryFun!("return a + b;")(41, 1) == 42);
565 
566     // Issue 9906
567     struct Seen
568     {
569         static bool opCall(int x, int y) { return true; }
570     }
571     static assert(is(typeof(naryFun!Seen)));
572     assert(naryFun!Seen(1,1));
573 
574     struct FuncObj
575     {
576         bool opCall(int x, int y) { return true; }
577     }
578     FuncObj fo;
579     static assert(!needOpCallAlias!fo);
580     static assert(is(typeof(naryFun!fo)));
581     assert(naryFun!fo(1,1));
582 
583     // Function object with non-static opCall can only be called with an
584     // instance, not with merely the type.
585     static assert(!is(typeof(naryFun!FuncObj)));
586 }
587 
588 
589 /++
590 N-ary predicate that reverses the order of arguments, e.g., given
591 `pred(a, b, c)`, returns `pred(c, b, a)`.
592 +/
593 template reverseArgs(alias fun)
594 {
595     import std.meta: Reverse;
596     ///
597     @optmath auto ref reverseArgs(Args...)(auto ref Args args)
598         if (is(typeof(fun(Reverse!args))))
599     {
600         return fun(Reverse!args);
601     }
602 
603 }
604 
605 ///
606 @safe version(mir_core_test) unittest
607 {
608     int abc(int a, int b, int c) { return a * b + c; }
609     alias cba = reverseArgs!abc;
610     assert(abc(91, 17, 32) == cba(32, 17, 91));
611 }
612 
613 @safe version(mir_core_test) unittest
614 {
615     int a(int a) { return a * 2; }
616     alias _a = reverseArgs!a;
617     assert(a(2) == _a(2));
618 }
619 
620 @safe version(mir_core_test) unittest
621 {
622     int b() { return 4; }
623     alias _b = reverseArgs!b;
624     assert(b() == _b());
625 }
626 
627 @safe version(mir_core_test) unittest
628 {
629     alias gt = reverseArgs!(naryFun!("a < b"));
630     assert(gt(2, 1) && !gt(1, 1));
631     int x = 42;
632     bool xyz(int a, int b) { return a * x < b / x; }
633     auto foo = &xyz;
634     foo(4, 5);
635     alias zyx = reverseArgs!(foo);
636     assert(zyx(5, 4) == foo(4, 5));
637 }
638 
639 /++
640 Negates predicate `pred`.
641 +/
642 template not(alias pred)
643 {
644     static if (!is(typeof(pred) : string) && !needOpCallAlias!pred)
645     ///
646     @optmath bool not(T...)(auto ref T args)
647     {
648         return !pred(args);
649     }
650     else
651         alias not = .not!(naryFun!pred);
652 }
653 
654 ///
655 @safe version(mir_core_test) unittest
656 {
657     import std.algorithm.searching : find;
658     import std.uni : isWhite;
659     string a = "   Hello, world!";
660     assert(find!(not!isWhite)(a) == "Hello, world!");
661 }
662 
663 @safe version(mir_core_test) unittest
664 {
665     assert(not!"a != 5"(5));
666     assert(not!"a != b"(5, 5));
667 
668     assert(not!(() => false)());
669     assert(not!(a => a != 5)(5));
670     assert(not!((a, b) => a != b)(5, 5));
671     assert(not!((a, b, c) => a * b * c != 125 )(5, 5, 5));
672 }
673 
674 private template _pipe(size_t n)
675 {
676     static if (n)
677     {
678         enum i = n - 1;
679         enum _pipe = "f[" ~ i.stringof ~ "](" ~ ._pipe!i ~ ")";
680     }
681     else
682         enum _pipe = "forward!args";
683 }
684 
685 private template _unpipe(alias fun)
686 {
687     import std.traits: TemplateArgsOf, TemplateOf;
688     static if (__traits(compiles, TemplateOf!fun))
689         static if (__traits(isSame, TemplateOf!fun, .pipe))
690             alias _unpipe = TemplateArgsOf!fun;
691         else
692             alias _unpipe = fun;
693     else
694         alias _unpipe = fun;
695 
696 }
697 
698 private enum _needNary(alias fun) = is(typeof(fun) : string) || needOpCallAlias!fun;
699 
700 /++
701 Composes passed-in functions `fun[0], fun[1], ...` returning a
702 function `f(x)` that in turn returns
703 `...(fun[1](fun[0](x)))...`. Each function can be a regular
704 functions, a delegate, a lambda, or a string.
705 +/
706 template pipe(fun...)
707 {
708     static if (fun.length != 1)
709     {
710         import std.meta: staticMap, Filter;
711         alias f = staticMap!(_unpipe, fun);
712         static if (f.length == fun.length && Filter!(_needNary, f).length == 0)
713         {
714             ///
715             @optmath auto ref pipe(Args...)(auto ref Args args)
716             {
717                 return mixin (_pipe!(fun.length));
718             }
719         }
720         else alias pipe = .pipe!(staticMap!(naryFun, f));
721     }
722     else alias pipe = naryFun!(fun[0]);
723 }
724 
725 ///
726 @safe version(mir_core_test) unittest
727 {
728     assert(pipe!("a + b", a => a * 10)(2, 3) == 50);
729 }
730 
731 /// `pipe` can return by reference.
732 version(mir_core_test) unittest
733 {
734     int a;
735     assert(&pipe!("a", "a")(a) == &a);
736 }
737 
738 /// Template bloat reduction
739 version(mir_core_test) unittest
740 {
741     enum  a = "a * 2";
742     alias b = e => e + 2;
743 
744     alias p0 = pipe!(pipe!(a, b), pipe!(b, a));
745     alias p1 = pipe!(a, b, b, a);
746 
747     static assert(__traits(isSame, p0, p1));
748 }
749 
750 @safe version(mir_core_test) unittest
751 {
752     import std.algorithm.comparison : equal;
753     import std.algorithm.iteration : map;
754     import std.array : split;
755     import std.conv : to;
756 
757     // First split a string in whitespace-separated tokens and then
758     // convert each token into an integer
759     assert(pipe!(split, map!(to!(int)))("1 2 3").equal([1, 2, 3]));
760 }
761 
762 
763 struct AliasCall(T, string methodName, TemplateArgs...)
764 {
765     T __this;
766     alias __this this;
767 
768     ///
769     auto lightConst()() const @property
770     {
771         import mir.qualifier;
772         return AliasCall!(LightConstOf!T, methodName, TemplateArgs)(__this.lightConst);
773     }
774 
775     ///
776     auto lightImmutable()() immutable @property
777     {
778         import mir.qualifier;
779         return AliasCall!(LightImmutableOf!T, methodName, TemplateArgs)(__this.lightImmutable);
780     }
781 
782     this()(auto ref T value)
783     {
784         __this = value;
785     }
786     auto ref opCall(Args...)(auto ref Args args)
787     {
788         import std.traits: TemplateArgsOf;
789         mixin("return __this." ~ methodName ~ (TemplateArgs.length ? "!TemplateArgs" : "") ~ "(forward!args);");
790     }
791 }
792 
793 /++
794 Replaces call operator (`opCall`) for the value using its method.
795 The funciton is designed to use with  $(NDSLICE, topology, vmap) or $(NDSLICE, topology, map).
796 Params:
797     methodName = name of the methods to use for opCall and opIndex
798     TemplateArgs = template arguments
799 +/
800 template aliasCall(string methodName, TemplateArgs...)
801 {
802     /++
803     Params:
804         value = the value to wrap
805     Returns:
806         wrapped value with implemented opCall and opIndex methods
807     +/
808     AliasCall!(T, methodName, TemplateArgs) aliasCall(T)(T value) @property
809     {
810         return typeof(return)(value);
811     }
812 
813     /// ditto
814     ref AliasCall!(T, methodName, TemplateArgs) aliasCall(T)(return ref T value) @property @trusted
815     {
816         return  *cast(typeof(return)*) &value;
817     }
818 }
819 
820 ///
821 @safe pure nothrow version(mir_core_test) unittest
822 {
823     static struct S
824     {
825         auto lightConst()() const @property { return S(); }
826 
827         auto fun(size_t ct_param = 1)(size_t rt_param) const
828         {
829             return rt_param + ct_param;
830         }
831     }
832 
833     S s;
834 
835     auto sfun = aliasCall!"fun"(s);
836     assert(sfun(3) == 4);
837 
838     auto sfun10 = aliasCall!("fun", 10)(s);   // uses fun!10
839     assert(sfun10(3) == 13);
840 }
841 
842 /++
843 +/
844 template recurseTemplatePipe(alias Template, size_t N, Args...)
845 {
846     static if (N == 0)
847         alias recurseTemplatePipe = Args;
848     else
849     {
850         alias recurseTemplatePipe = Template!(.recurseTemplatePipe!(Template, N - 1, Args));
851     }
852 }
853 
854 ///
855 @safe version(mir_core_test) unittest
856 {
857     // import mir.ndslice.topology: map;
858     alias map(alias fun) = a => a; // some template
859     static assert (__traits(isSame, recurseTemplatePipe!(map, 2, "a * 2"), map!(map!"a * 2")));
860 }
861 
862 /++
863 +/
864 template selfAndRecurseTemplatePipe(alias Template, size_t N, Args...)
865 {
866     static if (N == 0)
867         alias selfAndRecurseTemplatePipe = Args;
868     else
869     {
870         alias selfAndRecurseTemplatePipe = Template!(.selfAndRecurseTemplatePipe!(Template, N - 1, Args));
871     }
872 }
873 
874 ///
875 @safe version(mir_core_test) unittest
876 {
877     // import mir.ndslice.topology: map;
878     alias map(alias fun) = a => a; // some template
879     static assert (__traits(isSame, selfAndRecurseTemplatePipe!(map, 2, "a * 2"), map!(pipe!("a * 2", map!"a * 2"))));
880 }
881 
882 /++
883 +/
884 template selfTemplatePipe(alias Template, size_t N, Args...)
885 {
886     static if (N == 0)
887         alias selfTemplatePipe = Args;
888     else
889     {
890         alias selfTemplatePipe = Template!(.selfTemplatePipe!(Template, N - 1, Args));
891     }
892 }
893 
894 ///
895 @safe version(mir_core_test) unittest
896 {
897     // import mir.ndslice.topology: map;
898     alias map(alias fun) = a => a; // some template
899     static assert (__traits(isSame, selfTemplatePipe!(map, 2, "a * 2"), map!(pipe!("a * 2", map!"a * 2"))));
900 }