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.range,
37        std.string,
38        std.traits,
39        std.typecons;
40 
41 /**
42 このテンプレートは、$(M std.functional.unaryFun)や$(M std.functional.binaryFun)を一般化し、
43 N個の引数を取れるようにしたものです。
44 $(M unaryFun)や$(M binaryFun)のように文字列に対して作用する場合は、その文字列で表される関数となります。
45 対して、文字列以外を$(M naryFun)に適用した場合には、その対象へのaliasとなります。
46 
47 文字列による形式には、現在のところアルファベット及び数字に対応しています。
48 */
49 template naryFun(alias fun, int N = -1)
50 if(is(typeof(fun) == string))
51 {
52     auto ref naryFunAlphabet(T...)(auto ref T args)
53     {
54         static assert(T.length <= 26);
55         mixin(createAliasAlphabet(T.length));
56         return mixin(fun);
57     }
58 
59 
60     auto ref naryFunNumber(T...)(auto ref T args)
61     {
62         mixin(createAliasNumber(T.length));
63         return mixin(fun);
64     }
65 
66 
67     auto ref naryFun(T...)(auto ref T args)
68     if(!(N >= 0) || T.length == N)
69     {
70       static if(is(typeof({naryFunNumber(forward!args);})))
71         return naryFunNumber(forward!args);
72       else
73         return naryFunAlphabet(forward!args);
74     }
75 
76 
77     string createAliasAlphabet(size_t nparam)
78     {
79         auto app = appender!string();
80         foreach(i; 0 .. nparam)
81             app.formattedWrite("alias %s = args[%s];\n", cast(char)(i + 'a'), i);
82         return app.data;
83     }
84 
85 
86     string createAliasNumber(size_t nparam)
87     {
88         auto app = appender!string();
89         foreach(i; 0 .. nparam)
90             app.formattedWrite("alias _%1$s = args[%1$s];\n", i);
91         return app.data;
92     }
93 }
94 
95 /// ditto
96 template naryFun(alias fun, int N = -1)
97 if(!is(typeof(fun) == string))
98 {
99     auto ref naryFun(T...)(auto ref T args)
100     if(!(N >= 0) || T.length == N)
101     {
102         return fun(forward!args);
103     }
104 }
105 
106 ///
107 unittest
108 {
109     alias test1 = naryFun!"a";
110     assert(test1(1) == 1);
111     assert(test1(test1(2.0)) == 2.0);
112     static assert(is(typeof({test1(1, 1);})));  // OK
113                                                 // 最初の引数を返す関数だから。
114                                                 // 2つ目の引数は使用されない。
115 
116     static assert(!is(typeof({test1();})));     // NG
117 
118 
119     alias test1_1 = naryFun!("a", 1);       // 引数の数を1つとする
120     assert(test1_1(1) == 1);
121     assert(test1_1(test1(2.0)) == 2.0);
122     static assert(!is(typeof({test1_1(1, 1);})));  // NG
123 
124 
125     alias test1_2 = naryFun!("a", 2);       // 引数の数を2つとする
126     assert(test1_2(1, 1) == 1);
127     assert(test1_2(test1_2(2.0, 2), 1) == 2.0);
128     static assert(!is(typeof({test1_2(1);})));  // NG
129 
130 
131     alias test2 = naryFun!"b";
132     assert(test2(1, 2) == 2);
133     assert(test2(test2(1, "2"), test2(3.0, '4')) == '4');
134     static assert(!is(typeof({test2();})));
135     static assert(!is(typeof({test2(1);})));
136     static assert(is(typeof({test2(1, 1, 2.2);})));
137 
138 
139     // アルファベット
140     alias test3 = naryFun!"a + b + c";
141     assert(test3(1, 2, 3) == 6);
142 
143     import std.bigint;
144     assert(test3(BigInt(1), 2, 3) == BigInt(6));
145 
146 
147     // 数字
148     alias test4 = naryFun!"_0 + _1 + _2 + _3";
149     assert(test4(1, 2, 3, 4) == 10);
150 }
151 
152 
153 /**
154 ある関数funcの引数に、タプルを適用できるようにします。
155 */
156 template adaptTuple(alias func, T...)
157 {
158     auto _toRvalue(X)(ref X a)
159     {
160         return a;
161     }
162 
163 
164     string _toRvalueNargs(size_t N)
165     {
166         return format("return func(%(_toRvalue(arg.field[%s])%|,%));", iota(N));
167     }
168 
169 
170   static if(T.length > 0)
171   {
172     auto ref adaptTuple(X)(ref X arg)
173     if(is(Unqual!X : Tuple!U, U...) && is(typeof({Tuple!T a = Unqual!X.init;})))
174     {
175         return func(arg.field);
176     }
177 
178     auto ref adaptTuple(X)(X arg)
179     if(is(Unqual!X : Tuple!U, U...) && is(typeof({Tuple!T a = Unqual!X.init;})))
180     {
181         mixin(_toRvalueNargs(T.length));
182     }
183   }
184   else
185   {
186     auto ref adaptTuple(X)(ref X arg)
187     if(is(Unqual!X == Tuple!U, U...))
188     {
189         return func(arg.field);
190     }
191 
192 
193     auto ref adaptTuple(X)(X arg)
194     if(is(Unqual!X == Tuple!U, U...))
195     {
196         mixin(_toRvalueNargs(X.field.length));
197     }
198   }
199 }
200 
201 ///
202 unittest
203 {
204     static ref int func1(ref int a, float b, byte c)
205     {
206         return a;
207     }
208 
209     alias adpt1 = adaptTuple!func1;
210     auto t = tuple(4, 1.1f, cast(byte)2);
211     assert(&adpt1(t) == &(t.field[0]));
212 
213     // NG; adpt1の引数がlvalueじゃない(std.forwardのような転送)
214     static assert(!is(typeof({adpt1(tuple(4, 1.1f, cast(byte)2));})));
215 
216     // naryFun & 事前に型指定あり
217     alias adpt2 = adaptTuple!(naryFun!"a", int, float, byte);
218     assert(&adpt2(t) == &(t.field[0])); // forward性
219     assert(adpt2(tuple(4, 1.1f, cast(byte)2)) == t.field[0]);
220 
221     // NG; stringはfloatへ暗黙変換不可能
222     static assert(!is(typeof({adpt2(tuple(1, "foo", cast(byte)2));})));
223 
224     // OK; realはfloatへ暗黙変換可能
225     static assert(is(typeof({adpt2(tuple(1, 1.1L, cast(byte)2));})));
226     assert(adpt2(tuple(1, 1.1L, cast(byte)2)) == 1);
227 
228     const ct = t;
229     static assert(is(typeof(adpt2(ct)) == const));
230 
231     immutable it = t;
232     static assert(is(typeof(adpt2(it)) == immutable));
233 
234     shared st = t;
235     static assert(is(typeof(adpt2(st)) == shared));
236 
237 
238     assert(adaptTuple!(naryFun!"a")(tuple(1, 2, 3)) == 1);
239     assert(adaptTuple!(naryFun!"a", int)(tuple(1)) == 1);
240 }
241