1 /**
2  * Functional tooling
3  *
4  * Authors: Tristan Brice Velloza Kildaire (deavmi)
5  */
6 module niknaks.functional;
7 
8 import std.traits : isAssignable, isFunction, isDelegate, ParameterTypeTuple, ReturnType;
9 import std.functional : toDelegate;
10 
11 /** 
12  * Predicate for testing an input type
13  * against a condition and returning either
14  * `true` or `false`
15  *
16  * Params:
17  *    T = the input type
18  */
19 template Predicate(T)
20 {
21 	/**
22 	 * Parameterized delegate pointer
23 	 * taking in `T` and returning
24 	 * either `true` or `false`
25 	 */
26 	alias Predicate = bool delegate(T);
27 }
28 
29 /** 
30  * Given the symbol of a function or
31  * delegate this will return a new
32  * `Predicate` of it
33  *
34  * Params:
35  *   func = the symbol of the function
36  * or delegate to make a predicate of
37  */
38 template predicateOf(alias func)
39 if(isFunction!(func) || isDelegate!(func))
40 {
41 	static if(!__traits(isSame, ReturnType!(func), bool))
42 	{
43 		pragma(msg, "Predicates are required to have a return type of bool");
44 		static assert(false);
45 	}
46 
47 	// Obtain all paramaters
48 	private alias params = ParameterTypeTuple!(func);
49 
50 	static if(params.length != 1)
51 	{
52 		pragma(msg, "Predicates are required to have an arity of 1");
53 		static assert(false);
54 	}
55 
56 	// Obtain the predicate's input type
57 	private alias predicateParameterType = params[0];
58 
59 	// Created predicate delegate
60 	private Predicate!(predicateParameterType) del;
61 
62 	/** 
63 	 * Given the symbol of a function or
64 	 * delegate this will return a new
65 	 * `Predicate` of it
66 	 *
67 	 * Returns: the predicate
68 	 */
69 	Predicate!(predicateParameterType) predicateOf()
70 	{
71 		// If it is a function, first make it a delegate
72 		static if(isFunction!(func))
73 		{
74 			del = toDelegate(&func);
75 		}
76 		else
77 		{
78 			del = func;
79 		}
80 
81 		return del;
82 	}
83 }
84 
85 version(unittest)
86 {
87 	private bool isEven(int number)
88 	{
89 		return number%2==0;
90 	}
91 }
92 
93 /**
94  * Uses a `Predicate` which tests
95  * an integer input for evenness
96  *
97  * We create the predicate by
98  * passing in the symbol of the
99  * function or delegate we wish
100  * to use for testing truthiness
101  * to a template function
102  * `predicateOf!(alias)`
103  */
104 unittest
105 {
106 	Predicate!(int) pred = predicateOf!(isEven);
107 
108 	assert(pred(0) == true);
109 	assert(pred(1) == false);
110 
111 	bool delegate(int) isEvenDel = toDelegate(&isEven);
112 	pred = predicateOf!(isEvenDel);
113 
114 	assert(pred(0) == true);
115 	assert(pred(1) == false);
116 }
117 
118 /** 
119  * Default exception which is thrown
120  * when `get()` is called on an
121  * `Optional!(T)` which has no
122  * value set
123  */
124 public class OptionalException : Exception
125 {
126 	/** 
127 	 * Constructs a new `OptionalException`
128 	 * with the given message
129 	 *
130 	 * Params:
131 	 *   msg = the error text
132 	 */
133 	this(string msg)
134 	{
135 		super(msg);
136 	}
137 }
138 
139 /** 
140  * Optionals for a given type and 
141  * with a customizable exception
142  * to be thrown when a value is
143  * not present and `get()` is
144  * called.
145  *
146  * Params:
147  *    T = the value type
148  *    onEmptyGet = the `Throwable`
149  * to be called when `get()` is
150  * called and no value is present
151  */
152 template Optional(T, onEmptyGet = OptionalException, exceptionArgs...)
153 if(isAssignable!(Throwable, onEmptyGet) // && 
154 //    __traits(getVirtualMethods, onEmptyGet, "")[0]
155   ) // TODO: Check for this() with arity of 1 and string
156 {
157 	/** 
158 	 * The optional itself
159 	 */
160 	public struct Optional
161 	{
162 		/** 
163 		 * The value
164 		 */
165 		private T value;
166 
167 		/** 
168 		 * Flag for if value
169 		 * has been set or
170 		 * not
171 		 */
172 		private bool isSet = false;
173 
174 		/** 
175 		 * Constructs an optional with
176 		 * the value already set
177 		 *
178 		 * Params:
179 		 *   value = the value to set
180 		 */
181 		this(T value)
182 		{
183 			set(value);
184 		}
185 
186 		/** 
187 		 * Sets the optional's value
188 		 *
189 		 * Params:
190 		 *   value = the value to set
191 		 */
192 		public void set(T value)
193 		{
194 			this.value = value;
195 			this.isSet = true;
196 		}
197 
198 		/** 
199 		 * Checks if a value is present
200 		 * or not
201 		 *
202 		 * Returns: `true` if present,
203 		 * otherwise `false`
204 		 */
205 		public bool isPresent()
206 		{
207 			return isSet;
208 		}
209 
210 		/** 
211 		 * Checks if there is
212 		 * no value present
213 		 *
214 		 * Returns: `true` if
215 		 * no value is present,
216 		 * `false` otherwise
217 		 */
218 		public bool isEmpty()
219 		{
220 			return isPresent() == false;
221 		}
222 
223 		/** 
224 		 * Returns the value of this
225 		 * optional if it is set. If
226 		 * not set then an exception
227 		 * is thrown.
228 		 *
229 		 * Returns: the value
230 		 * Throws:
231 		 *    Throwable if no value
232 		 * is present
233 		 */
234 		public T get()
235 		{
236 			if(!isPresent())
237 			{
238 				static if(exceptionArgs.length)
239 				{
240 					throw new onEmptyGet(exceptionArgs);
241 				}
242 				else
243 				{
244 					throw new onEmptyGet("Optional has no value yet get was called");
245 				}
246 			}
247 
248 			return value;
249 		}
250 
251 		/** 
252 		 * Creates an empty optional
253 		 *
254 		 * This is the same as doing `Optional!(T)()`
255 		 * or simply declaring a variable
256 		 * of the type `Optional!(T)
257 		 *
258 		 * Returns: an empty optional
259 		 */
260 		public static Optional!(T) empty()
261 		{
262 			return Optional!(T)();
263 		}
264 	}
265 }
266 
267 /**
268  * Creating an `Optional!(T)` with no 
269  * value present and then trying to
270  * get the value, which results in
271  * an exception
272  */
273 unittest
274 {
275 	struct MyType
276 	{
277 
278 	}
279 
280 	Optional!(MyType) d;
281 	assert(d.isPresent() == false);
282 	assert(d.isEmpty());
283 	
284 	d = Optional!(MyType)();
285 	assert(d.isPresent() == false);
286 	assert(d.isEmpty());
287 
288 	d = Optional!(MyType).empty();
289 	assert(d.isPresent() == false);
290 	assert(d.isEmpty());
291 
292 	try
293 	{
294 		d.get();
295 		assert(false);
296 	}
297 	catch(OptionalException)
298 	{
299 		assert(true);
300 	}
301 }
302 
303 /**
304  * Creating an `Optional!(T)` with a
305  * value present and then trying to
306  * get the value, which results in
307  * said value being returned
308  */
309 unittest
310 {
311 	Optional!(byte) f = Optional!(byte)(1);
312 	assert(f.isPresent() == true);
313 	assert(f.isEmpty() == false);
314 
315 	try
316 	{
317 		assert(1 == f.get());
318 	}
319 	catch(OptionalException)
320 	{
321 		assert(false);
322 	}
323 }
324 
325 /** 
326  * A result type
327  */
328 @safe @nogc
329 public struct Result(Okay, Error)
330 {
331 	private Okay okay_val;
332 	private Error error_val;
333 
334 	private bool isSucc;
335 	
336 	@disable
337 	private this();
338 
339 	private this(bool isSucc)
340 	{
341 		this.isSucc = isSucc;
342 	}
343 
344 	/** 
345 	 * Retuns the okay value
346 	 *
347 	 * Returns: the value
348 	 */
349 	public Okay ok()
350 	{
351 		return this.okay_val;
352 	}
353 
354 	/** 
355 	 * Returns the error value
356 	 *
357 	 * Returns: the value
358 	 */
359 	public Error error()
360 	{
361 		return this.error_val;
362 	}
363 
364 	/** 
365 	 * Returns the okayness of
366 	 * this result
367 	 *
368 	 * See_Also: `is_okay`
369 	 * Returns: a boolean
370 	 */
371 	public bool opCast(T)()
372 	if(__traits(isSame, T, bool))
373 	{
374 		return is_okay();
375 	}
376 
377 	/** 
378 	 * Check if is okay
379 	 *
380 	 * Returns: `true` if
381 	 * okay, `false` otherwise
382 	 */
383 	public bool is_okay()
384 	{
385 		return this.isSucc == true;
386 	}
387 
388 	/** 
389 	 * Check if is erroneous
390 	 *
391 	 * Returns: `true` if
392 	 * erroneous, `false`
393 	 * otherwise
394 	 */
395 	public bool is_error()
396 	{
397 		return this.isSucc == false;
398 	}
399 }
400 
401 /** 
402  * Constructs a new `Result` with the
403  * status set to okay and with the
404  * provided value.
405  *
406  * If you don't specify the type
407  * of the error value for this
408  * then it is assumed to be the
409  * same as the okay type.
410  *
411  * Params:
412  *   okayVal = the okay value
413  * Returns: a `Result`
414  */
415 @safe @nogc
416 public static Result!(OkayType, ErrorType) ok(OkayType, ErrorType = OkayType)(OkayType okayVal)
417 {
418 	Result!(OkayType, ErrorType) result = Result!(OkayType, ErrorType)(true);
419 	result.okay_val = okayVal;
420 
421 	return result;
422 }
423 
424 /** 
425  * Constructs a new `Result` with the
426  * status set to error and with the
427  * provided value.
428  *
429  * If you don't specify the type
430  * of the okay value for this
431  * then it is assumed to be the
432  * same as the error type.
433  *
434  * Params:
435  *   errorVal = the error value
436  * Returns: a `Result`
437  */
438 @safe @nogc
439 public static Result!(OkayType, ErrorType) error(ErrorType, OkayType = ErrorType)(ErrorType errorVal)
440 {
441 	Result!(OkayType, ErrorType) result = Result!(OkayType, ErrorType)(false);
442 	result.error_val = errorVal;
443 
444 	return result;
445 }
446 
447 /**
448  * Tests the usage of okay
449  * result types
450  */
451 unittest
452 {
453 	auto a = ok("A successful result");
454 	assert(a.ok == "A successful result");
455 	assert(a.error == null);
456 
457 	// Should be Result!(string, string)
458 	static assert(__traits(isSame, typeof(a.okay_val), string));
459 	static assert(__traits(isSame, typeof(a.error_val), string));
460 
461 	// opCast to bool
462 	assert(cast(bool)a);
463 
464 	// Validity checking
465 	assert(a.is_okay());
466 	assert(!a.is_error());
467 	
468 	auto b = ok!(string, Exception)("A successful result");
469 	assert(b.ok == "A successful result");
470 	assert(b.error is null);
471 
472 	// Should be Result!(string, Exception)
473 	static assert(__traits(isSame, typeof(b.okay_val), string));
474 	static assert(__traits(isSame, typeof(b.error_val), Exception));
475 }
476 
477 /**
478  * Tests the usage of error
479  * result types
480  */
481 unittest
482 {
483 	auto a = error(new Exception("A failed result"));
484 	assert(a.ok is null);
485 	assert(cast(Exception)a.error && (cast(Exception)a.error).msg == "A failed result");
486 
487 	// Should be Result!(Exception, Exception)
488 	static assert(__traits(isSame, typeof(a.okay_val), Exception));
489 	static assert(__traits(isSame, typeof(a.error_val), Exception));
490 
491 	// opCast to bool
492 	assert(!cast(bool)a);
493 
494 	// Validity checking
495 	assert(!a.is_okay());
496 	assert(a.is_error());
497 	
498 	auto b = error!(Exception, string)(new Exception("A failed result"));
499 	assert(a.ok is null);
500 	assert(cast(Exception)a.error && (cast(Exception)a.error).msg == "A failed result");
501 
502 	// Should be Result!(string, Exception)
503 	static assert(__traits(isSame, typeof(b.okay_val), string));
504 	static assert(__traits(isSame, typeof(b.error_val), Exception));
505 }