1 // Written in the D programming language.
2 /*
3 NYSL Version 0.9982
4 
5 A. This software is "Everyone'sWare". It means:
6   Anybody who has this software can use it as if he/she is
7   the author.
8 
9   A-1. Freeware. No fee is required.
10   A-2. You can freely redistribute this software.
11   A-3. You can freely modify this software. And the source
12       may be used in any software with no limitation.
13   A-4. When you release a modified version to public, you
14       must publish it with your name.
15 
16 B. The author is not responsible for any kind of damages or loss
17   while using or misusing this software, which is distributed
18   "AS IS". No warranty of any kind is expressed or implied.
19   You use AT YOUR OWN RISK.
20 
21 C. Copyrighted to Kazuki KOMATSU
22 
23 D. Above three clauses are applied both to source and binary
24   form of this software.
25 */
26 
27 /**
28 このモジュールは、標準ライブラリのstd.functionalを強化します。
29 */
30 
31 module carbon.functional;
32 
33 import std.algorithm,
34        std.array,
35        std.format,
36        std.functional,
37        std.range,
38        std..string,
39        std.traits,
40        std.typecons;
41 
42 /**
43 このテンプレートは、$(M std.functional.unaryFun)や$(M std.functional.binaryFun)を一般化し、
44 N個の引数を取れるようにしたものです。
45 $(M unaryFun)や$(M binaryFun)のように文字列に対して作用する場合は、その文字列で表される関数となります。
46 対して、文字列以外を$(M naryFun)に適用した場合には、その対象へのaliasとなります。
47 
48 文字列による形式には、現在のところアルファベット及び数字に対応しています。
49 */
50 template naryFun(alias fun, int N = -1)
51 if(is(typeof(fun) == string))
52 {
53     auto ref naryFunAlphabet(T...)(auto ref T args)
54     {
55         static assert(T.length <= 26);
56         mixin(createAliasAlphabet(T.length));
57         return mixin(fun);
58     }
59 
60 
61     auto ref naryFunNumber(T...)(auto ref T args)
62     {
63         mixin(createAliasNumber(T.length));
64         return mixin(fun);
65     }
66 
67 
68     auto ref naryFun(T...)(auto ref T args)
69     if(!(N >= 0) || T.length == N)
70     {
71       static if(is(typeof({naryFunNumber(forward!args);})))
72         return naryFunNumber(forward!args);
73       else
74         return naryFunAlphabet(forward!args);
75     }
76 
77 
78     string createAliasAlphabet(size_t nparam)
79     {
80         auto app = appender!string();
81         foreach(i; 0 .. nparam)
82             app.formattedWrite("alias %s = args[%s];\n", cast(char)(i + 'a'), i);
83         return app.data;
84     }
85 
86 
87     string createAliasNumber(size_t nparam)
88     {
89         auto app = appender!string();
90         foreach(i; 0 .. nparam)
91             app.formattedWrite("alias _%1$s = args[%1$s];\n", i);
92         return app.data;
93     }
94 }
95 
96 /// ditto
97 template naryFun(alias fun, int N = -1)
98 if(!is(typeof(fun) == string))
99 {
100     auto ref naryFun(T...)(auto ref T args)
101     if(!(N >= 0) || T.length == N)
102     {
103         return fun(forward!args);
104     }
105 }
106 
107 ///
108 unittest
109 {
110     alias test1 = naryFun!"a";
111     assert(test1(1) == 1);
112     assert(test1(test1(2.0)) == 2.0);
113     static assert(is(typeof({test1(1, 1);})));  // OK
114                                                 // 最初の引数を返す関数だから。
115                                                 // 2つ目の引数は使用されない。
116 
117     static assert(!is(typeof({test1();})));     // NG
118 
119 
120     alias test1_1 = naryFun!("a", 1);       // 引数の数を1つとする
121     assert(test1_1(1) == 1);
122     assert(test1_1(test1(2.0)) == 2.0);
123     static assert(!is(typeof({test1_1(1, 1);})));  // NG
124 
125 
126     alias test1_2 = naryFun!("a", 2);       // 引数の数を2つとする
127     assert(test1_2(1, 1) == 1);
128     assert(test1_2(test1_2(2.0, 2), 1) == 2.0);
129     static assert(!is(typeof({test1_2(1);})));  // NG
130 
131 
132     alias test2 = naryFun!"b";
133     assert(test2(1, 2) == 2);
134     assert(test2(test2(1, "2"), test2(3.0, '4')) == '4');
135     static assert(!is(typeof({test2();})));
136     static assert(!is(typeof({test2(1);})));
137     static assert(is(typeof({test2(1, 1, 2.2);})));
138 
139 
140     // アルファベット
141     alias test3 = naryFun!"a + b + c";
142     assert(test3(1, 2, 3) == 6);
143 
144     import std.bigint;
145     assert(test3(BigInt(1), 2, 3) == BigInt(6));
146 
147 
148     // 数字
149     alias test4 = naryFun!"_0 + _1 + _2 + _3";
150     assert(test4(1, 2, 3, 4) == 10);
151 }
152 
153 
154 /**
155 ある関数funcの引数に、タプルを適用できるようにします。
156 */
157 template adaptTuple(alias func, T...)
158 {
159     auto _toRvalue(X)(ref X a)
160     {
161         return a;
162     }
163 
164 
165     string _toRvalueNargs(size_t N)
166     {
167         return format("return func(%(_toRvalue(arg.field[%s])%|,%));", iota(N));
168     }
169 
170 
171   static if(T.length > 0)
172   {
173     auto ref adaptTuple(X)(ref X arg)
174     if(is(Unqual!X : Tuple!U, U...) && is(typeof({Tuple!T a = Unqual!X.init;})))
175     {
176         return func(arg.field);
177     }
178 
179     auto ref adaptTuple(X)(X arg)
180     if(is(Unqual!X : Tuple!U, U...) && is(typeof({Tuple!T a = Unqual!X.init;})))
181     {
182         mixin(_toRvalueNargs(T.length));
183     }
184   }
185   else
186   {
187     auto ref adaptTuple(X)(ref X arg)
188     if(is(Unqual!X == Tuple!U, U...))
189     {
190         return func(arg.field);
191     }
192 
193 
194     auto ref adaptTuple(X)(X arg)
195     if(is(Unqual!X == Tuple!U, U...))
196     {
197         mixin(_toRvalueNargs(X.field.length));
198     }
199   }
200 }
201 
202 ///
203 unittest
204 {
205     static ref int func1(ref int a, float b, byte c)
206     {
207         return a;
208     }
209 
210     alias adpt1 = adaptTuple!func1;
211     auto t = tuple(4, 1.1f, cast(byte)2);
212     assert(&adpt1(t) == &(t.field[0]));
213 
214     // NG; adpt1の引数がlvalueじゃない(std.forwardのような転送)
215     static assert(!is(typeof({adpt1(tuple(4, 1.1f, cast(byte)2));})));
216 
217     // naryFun & 事前に型指定あり
218     alias adpt2 = adaptTuple!(naryFun!"a", int, float, byte);
219     assert(&adpt2(t) == &(t.field[0])); // forward性
220     assert(adpt2(tuple(4, 1.1f, cast(byte)2)) == t.field[0]);
221 
222     // NG; stringはfloatへ暗黙変換不可能
223     static assert(!is(typeof({adpt2(tuple(1, "foo", cast(byte)2));})));
224 
225     // OK; realはfloatへ暗黙変換可能
226     static assert(is(typeof({adpt2(tuple(1, 1.1L, cast(byte)2));})));
227     assert(adpt2(tuple(1, 1.1L, cast(byte)2)) == 1);
228 
229     const ct = t;
230     static assert(is(typeof(adpt2(ct)) == const));
231 
232     immutable it = t;
233     static assert(is(typeof(adpt2(it)) == immutable));
234 
235     shared st = t;
236     static assert(is(typeof(adpt2(st)) == shared));
237 
238 
239     assert(adaptTuple!(naryFun!"a")(tuple(1, 2, 3)) == 1);
240     assert(adaptTuple!(naryFun!"a", int)(tuple(1)) == 1);
241 }
242 
243 
244 /**
245 関数を信頼関数にします
246 */
247 auto ref assumeTrusted(alias fn, T...)(auto ref T args) @trusted
248 {
249     return naryFun!fn(forward!args);
250 }
251 
252 
253 /**
254 
255 */
256 auto ref passTo(alias f, T...)(auto ref T args)
257 {
258     f(forward!args);
259     return args[0];
260 }