## digitalmars.D.learn - Recursive Algebraic

• Mr.Bingo (80/80) Jun 30 2018 I'm trying to create a vector of vectors(more general than
• Mr.Bingo (88/88) Jun 30 2018 The problem is that it seems that when one has a parameterized
```I'm trying to create a vector of vectors(more general than
vectors or matrices).

The idea is that a Vector can contain another Vector or another
type. Vector can be specified to be fixed in length or dynamic
for efficiency. Vector!(T, N) creates a vector of leaf type T and
length N. If N = size_t.max then the vector internally uses a
dynamic array.

Vector is essentially a tree. Norm, dot, and many operators on
Vector are computed recursively. The norm of a vector of vectors
is the norm of the vector of the norm of vectors. This definition
is valid when Vector is a simple Vector but also works for
vectors of vectors. Other operators can be similarly defined.

I believe the problem is that Algebraic only accepts This of the
fixed N. So as of now one can only have vectors of vectors with
size 1 or fixed N.

I also get an error

overload for type 'VariantN!(4u, int, This*)*' hasn't been
specified".

It would be nice if the compiler would construct the missing
handler and give the signature so I could know what I'm doing
wrong, I'm using (This* i) => ; but it does not recognize that.

import std.typecons, std.range, std.array, std.algorithm,
std.variant, std.stdio, std.traits;

struct Vector(T, size_t N = size_t.max) // N could be small value
if it is stored inside the vector as it could be stored as part
of the flags
{
Flags flags; // Contains direction flag(row or column),
normalized, etc

import std.range, std.typecons, std.meta, std.algorithm,
std.conv, std.math;

static if (N == size_t.max)		// For size_t.max sets N to be
infinite/dynamic;
{
mixin("Algebraic!(T, This*)[] data;");
property size_t Length() { return data.length; }
}
else
{
mixin("Algebraic!(T, This*)[N] data;");
static  property size_t Length() { return N; }
}
alias data this;

property double Norm(size_t n = 2)
{
return iota(0, n).map!((a)
{
return data[a].visit!(
(T i) { return 1; },
(This* i) { return 2000; }
);
}).sum();
}

auto opDispatch(string s, Args...)(Args v)
{

enum index = to!int(s[1..\$]);

static if (N == size_t.max) while(index >= data.length) data ~=
T.init;

static if (Args.length == 0)
return data[index];
else static if (Args.length == 1)
{
data[index] = &v;
}

}

}

void main()
{
import std.math, std.variant;

Vector!(int, 5) v;
v.x1 = v;
writeln(v.x1);
v.x2 = 4;
v.x3 = 5;
writeln(v.x3);
writeln(v.Norm);

getchar();

}
```
Jun 30 2018
```The problem is that it seems that when one has a parameterized
type, you must completely specify the parameters when using
Algebraic,

Algebraic!(T, Vector!int, Vector!(double, 3), Vector!(double, 3),
...)[] data;

to be able to encapsulate an Algebraic on Vector(as a collection
of all fixed point evaluations).

What I'd rather do is something akin to

Algebraic!(T, Vector!(T, N))[] data;

or, hell, even just

Algebraic!(T, Vector)[] data;

Since A!B bears no relationship to A!C except their name, this is
not necessarily a good idea, but neither is having to explicitly
express all kinds.

I imagine there is some trick using inheritance,

Maybe

class X;
class A(int N) : X;

then

Algebraic!(T, X)[] data;

only works if Algebraic handles inheritance and checks with
handlers for specificity.

The problem with D's type system is that it does not allow one to
express sets of types, which is a special type in and of itself,
except in some specific cases such as using is(,...).

Here is a snippet of code

https://dpaste.dzfl.pl/8ff1cd3d7d46

That shows that an algebraic does not handle inheritance.

if Y : X then we'd expect

Algebraic!(T, Y) : Algebraic!(T, X)

and for there to be no problem.

For this not to be a problem two things need to be known:

1. To be able to get the type of X and Y
2. To be able to determine if Y : X.

D provides the ability to determine if a type inherits from
another at runtime using classinfo which every D class contains
and it's inheritance relationships, solving problem 2.

Problem 1 is to be able get the types of an object in a way that
can be related to D's pre-existing methods for comparing type
info. Variant stores the typeid so Problem 2 is solved!

Therefor, we can safely say that Algebraic can easily deal with
inheritance. I am not familiar enough with the D internals to
provide a fix. Anyone familiar with Algebraic should be able to
write a few lines of code to allow for inheritance checking(or
even complex patterns using a lambda to determine if a type is in
the Algebraic. This becomes a function on the typeid's and
classinfo's which is determined by the user.

Algebraic!((x) {
if (is(x.type == typeid(y)))
return x.get!X;
return defaultX(x);
}, int)

Where the above algebraic allows any object that has the same
typeid of some object y or some object that has the same type as
defaultX(x)(which could be anything) or an int.

One could even do strange things like

Algebraic!((x) {
if (rand1(0.5)
return x;
return 1;
}, int)

which, remembering that this lambda is run at runtime to
determine if an object stored in the variant is "in the
algebraic", will allow any object to pass 50% of the time(and
potentially crash when some code gets an object it can't handle.

For example, if the above algebraic was to work only integral
types then it would be problematic when x was a non integral
type(but maybe floating point t types would work). This is
assuming some handler was called, which wouldn't be because there
is no handler that exists handle the non integral types.

Hence things like visit would have to allow, if the lambda was
specified, a way to fall through.

a.visit!((int i) => 4*i, (Object o) => return 2);

Which, 50% of the time would return 2 and the other half of the
time it would return 4.

Allowing functions to specify Algebraic relationships gives far
more power. D seems to already have everything available to do
it. For example, the original problem of inheritance
relationships can easily be expressed:

Algebraic!((x) {
if (doesDerive!Y(x))
return cast(Y)x;
if (doesDerive!Z(x))
return new Q(x);
return null;
}, int)

The algebraic handles an object derived from Y, Q, and int.

a.visit!((int i) => 4*i, (Y) => 2, (Q q) => q.value);
```
Jun 30 2018