mirror of https://github.com/deavmi/niknaks
281 lines
4.8 KiB
D
281 lines
4.8 KiB
D
/**
|
|
* Functional tooling
|
|
*
|
|
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
|
*/
|
|
module niknaks.functional;
|
|
|
|
import std.traits : isAssignable, isFunction, isDelegate, ParameterTypeTuple, ReturnType;
|
|
import std.functional : toDelegate;
|
|
|
|
/**
|
|
* Predicate for testing an input type
|
|
* against a condition and returning either
|
|
* `true` or `false`
|
|
*
|
|
* Params:
|
|
* T = the input type
|
|
*/
|
|
template Predicate(T)
|
|
{
|
|
/**
|
|
* Parameterized delegate pointer
|
|
* taking in `T` and returning
|
|
* either `true` or `false`
|
|
*/
|
|
alias Predicate = bool delegate(T);
|
|
}
|
|
|
|
/**
|
|
* Given the symbol of a function or
|
|
* delegate this will return a new
|
|
* `Predicate` of it
|
|
*
|
|
* Params:
|
|
* func = the symbol of the function
|
|
* or delegate to make a predicate of
|
|
*/
|
|
template predicateOf(alias func)
|
|
if(isFunction!(func) || isDelegate!(func))
|
|
{
|
|
static if(!__traits(isSame, ReturnType!(func), bool))
|
|
{
|
|
pragma(msg, "Predicates are required to have a return type of bool");
|
|
static assert(false);
|
|
}
|
|
|
|
// Obtain all paramaters
|
|
private alias params = ParameterTypeTuple!(func);
|
|
|
|
static if(params.length != 1)
|
|
{
|
|
pragma(msg, "Predicates are required to have an arity of 1");
|
|
static assert(false);
|
|
}
|
|
|
|
// Obtain the predicate's input type
|
|
private alias predicateParameterType = params[0];
|
|
|
|
// Created predicate delegate
|
|
private Predicate!(predicateParameterType) del;
|
|
|
|
/**
|
|
* Given the symbol of a function or
|
|
* delegate this will return a new
|
|
* `Predicate` of it
|
|
*
|
|
* Returns: the predicate
|
|
*/
|
|
Predicate!(predicateParameterType) predicateOf()
|
|
{
|
|
// If it is a function, first make it a delegate
|
|
static if(isFunction!(func))
|
|
{
|
|
del = toDelegate(&func);
|
|
}
|
|
else
|
|
{
|
|
del = func;
|
|
}
|
|
|
|
return del;
|
|
}
|
|
}
|
|
|
|
version(unittest)
|
|
{
|
|
private bool isEven(int number)
|
|
{
|
|
return number%2==0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Uses a `Predicate` which tests
|
|
* an integer input for evenness
|
|
*
|
|
* We create the predicate by
|
|
* passing in the symbol of the
|
|
* function or delegate we wish
|
|
* to use for testing truthiness
|
|
* to a template function
|
|
* `predicateOf!(alias)`
|
|
*/
|
|
unittest
|
|
{
|
|
Predicate!(int) pred = predicateOf!(isEven);
|
|
|
|
assert(pred(0) == true);
|
|
assert(pred(1) == false);
|
|
|
|
bool delegate(int) isEvenDel = toDelegate(&isEven);
|
|
pred = predicateOf!(isEvenDel);
|
|
|
|
assert(pred(0) == true);
|
|
assert(pred(1) == false);
|
|
}
|
|
|
|
/**
|
|
* Default exception which is thrown
|
|
* when `get()` is called on an
|
|
* `Optional!(T)` which has no
|
|
* value set
|
|
*/
|
|
public class OptionalException : Exception
|
|
{
|
|
/**
|
|
* Constructs a new `OptionalException`
|
|
* with the given message
|
|
*
|
|
* Params:
|
|
* msg = the error text
|
|
*/
|
|
this(string msg)
|
|
{
|
|
super(msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Optionals for a given type and
|
|
* with a customizable exception
|
|
* to be thrown when a value is
|
|
* not present and `get()` is
|
|
* called.
|
|
*
|
|
* Params:
|
|
* T = the value type
|
|
* onEmptyGet = the `Throwable`
|
|
* to be called when `get()` is
|
|
* called and no value is present
|
|
*/
|
|
template Optional(T, onEmptyGet = OptionalException, exceptionArgs...)
|
|
if(isAssignable!(Throwable, onEmptyGet) // &&
|
|
// __traits(getVirtualMethods, onEmptyGet, "")[0]
|
|
) // TODO: Check for this() with arity of 1 and string
|
|
{
|
|
/**
|
|
* The optional itself
|
|
*/
|
|
public struct Optional
|
|
{
|
|
/**
|
|
* The value
|
|
*/
|
|
private T value;
|
|
|
|
/**
|
|
* Flag for if value
|
|
* has been set or
|
|
* not
|
|
*/
|
|
private bool isSet = false;
|
|
|
|
/**
|
|
* Constructs an optional with
|
|
* the value already set
|
|
*
|
|
* Params:
|
|
* value = the value to set
|
|
*/
|
|
this(T value)
|
|
{
|
|
set(value);
|
|
}
|
|
|
|
/**
|
|
* Sets the optional's value
|
|
*
|
|
* Params:
|
|
* value = the value to set
|
|
*/
|
|
public void set(T value)
|
|
{
|
|
this.value = value;
|
|
this.isSet = true;
|
|
}
|
|
|
|
/**
|
|
* Checks if a value is present
|
|
* or not
|
|
*
|
|
* Returns: `true` if present,
|
|
* otherwise `false`
|
|
*/
|
|
public bool isPresent()
|
|
{
|
|
return isSet;
|
|
}
|
|
|
|
/**
|
|
* Returns the value of this
|
|
* optional if it is set. If
|
|
* not set then an exception
|
|
* is thrown.
|
|
*
|
|
* Returns: the value
|
|
* Throws:
|
|
* Throwable if no value
|
|
* is present
|
|
*/
|
|
public T get()
|
|
{
|
|
if(!isPresent())
|
|
{
|
|
static if(exceptionArgs.length)
|
|
{
|
|
throw new onEmptyGet(exceptionArgs);
|
|
}
|
|
else
|
|
{
|
|
throw new onEmptyGet("Optional has no value yet get was called");
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creating an `Optional!(T)` with no
|
|
* value present and then trying to
|
|
* get the value, which results in
|
|
* an exception
|
|
*/
|
|
unittest
|
|
{
|
|
Optional!(int) d;
|
|
assert(d.isPresent() == false);
|
|
|
|
try
|
|
{
|
|
d.get();
|
|
assert(false);
|
|
}
|
|
catch(OptionalException)
|
|
{
|
|
assert(true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creating an `Optional!(T)` with a
|
|
* value present and then trying to
|
|
* get the value, which results in
|
|
* said value being returned
|
|
*/
|
|
unittest
|
|
{
|
|
Optional!(byte) f = Optional!(byte)(1);
|
|
assert(f.isPresent() == true);
|
|
|
|
try
|
|
{
|
|
assert(1 == f.get());
|
|
}
|
|
catch(OptionalException)
|
|
{
|
|
assert(false);
|
|
}
|
|
} |