www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Recursive Algebraic

reply Mr.Bingo <Bingo Namo.com> writes:
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[0];			
		}

	}

	
}

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
parent Mr.Bingo <Bingo Namo.com> writes:
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