On 06/02/2016 05:16 PM, Alex wrote:
What I mean is: if there would be a possibility to use algebraic types
here (or maybe some kind of template...), then my types would be able
(maybe! did not seen anything similar yet...) to choose the proper
methods for themselves automatically:
As long as my type has a length > 1 it is handled as a range, if the
range has the length=1 it is handled as a single element and not as a
range, if the range has the length=0 it is handled as an absent element.
I may be getting what you're up to. Maybe not.
So we start with something like this (using arrays instead of arbitrary
ranges to keep things simple):
import std.stdio: writeln;
void main()
{
f([]);
f([1]);
f([1, 2]);
}
void f(int[] a)
{
if (a.length == 0) f_none(a);
else if (a.length == 1) f_one(a);
else f_many(a);
}
void f_none(int[] a)
{
writeln("nothing to see here");
}
void f_one(int[] a)
{
assert(a.length == 1);
writeln("exactly one: ", a[0]);
}
void f_many(int[] a)
{
assert(a.length >= 2);
writeln("at least two: ", a[0 .. 2]);
}
And instead you'd like something more like this:
import std.stdio: writeln;
import std.variant: Algebraic;
void main()
{
f([]);
f([1]);
f([1, 2]);
}
void f(int[] arr)
{
A a = arrayToA(arr);
foreach (T; A.AllowedTypes)
{
if (T* p = a.peek!T) f_impl(*p);
}
}
void f_impl(Empty e) { writeln("nothing to see here"); }
void f_impl(int a) { writeln("exactly one: ", a); }
void f_impl(Many!int a) { writeln("at least two: ", a[0 .. 2]); }
struct Empty {}
struct Many(T)
{
T[] arr;
alias arr this;
/* Somehow it's enforced here that arr.length >= 2. */
}
alias A = Algebraic!(Empty, int, Many!int);
A arrayToA(int[] a)
{
A result;
switch (a.length)
{
case 0: result = Empty.init; break;
case 1: result = a[0]; break;
default: result = Many!int(a);
}
return result;
}
And the advantages of it are:
* Don't need asserts in the different `f_impl`s.
* The branching in f is guided by the parameter types of f_impl (could
possibly be extracted, instead of being hardcoded manually).
Tell me if I'm way off.