TypeFunction example creatiing a conversion matrix
Adam D. Ruppe
destructionator at gmail.com
Thu Oct 1 23:02:58 UTC 2020
On Thursday, 1 October 2020 at 19:15:10 UTC, Andrei Alexandrescu
wrote:
> Problem definition: Implement Variant.get correctly. :o)
>
> E.g. this produces a type error somewhere in the innards of
> std.variant: https://run.dlang.io/is/joIbeV. I'm not sure how
> many other cases are out there.
So here's my proof of concept that this is doable today:
---------------
import std.typecons; // for Rebindable
interface MyTypeInfo {
const pure nothrow: // yadda yadda yada
bool implicitlyConvertsTo(const MyTypeInfo t);
bool isNullable();
string toiString();
}
interface MyTypeInfoFunction {
const pure nothrow:
immutable(MyTypeInfo) returnType();
immutable(MyTypeInfo)[] paramsTypes();
}
interface MyTypeInfoClass {
const pure nothrow:
immutable(MyTypeInfo)[] bases();
}
class MyTypeInfoImpl_FunctionPointer(T) : MyTypeInfo,
MyTypeInfoFunction {
const:
bool isNullable() { return true; }
string toiString() { return T.stringof; }
static if(is(T == R function(Params), R, Params...)) {
immutable(MyTypeInfo) returnType() {
return mytypeid!R;
}
immutable(MyTypeInfo)[] paramsTypes() pure nothrow {
import std.meta; // I wouldn't normally use this but meh it is
a demo
static immutable MyTypeInfo[] result = [staticMap!(mytypeid,
Params)];
return result[];
}
} else static assert(0, "mistake in " ~ T.stringof);
bool implicitlyConvertsTo(const MyTypeInfo t) {
if(this is t)
return true;
if(t is mytypeid!(typeof(null)))
return true;
auto fp = cast(MyTypeInfoFunction) t;
if(fp is null)
return false;
if(!returnType.implicitlyConvertsTo(fp.returnType))
return false;
if(paramsTypes.length != fp.paramsTypes.length)
return false;
foreach(idx, arg; fp.paramsTypes)
if(!arg.implicitlyConvertsTo(paramsTypes[idx]))
return false;
return true;
}
}
class MyTypeInfoImpl_Class(T) : MyTypeInfo, MyTypeInfoClass {
bool isNullable() const { return true; }
string toiString() const { return T.stringof; }
static if(is(T Super == super))
immutable(MyTypeInfo)[] bases() const {
import std.meta;
static immutable MyTypeInfo[] result = [staticMap!(mytypeid,
Super)];
return result[];
}
bool implicitlyConvertsTo(const MyTypeInfo t) const {
if(this is t)
return true;
if(t is mytypeid!(typeof(null)))
return true;
foreach(base; bases)
if(t is base)
return true;
return false;
}
}
class MyTypeInfoImpl(T) : MyTypeInfo {
bool isNullable() const {
return is(typeof(null) : T);
}
string toiString() const {
return T.stringof;
}
bool implicitlyConvertsTo(const MyTypeInfo t) const {
static if(is(T == typeof(null)))
return t.isNullable();
return this is t;
}
}
template mytypeid(T) {
static if(is(T == return))
immutable MyTypeInfo mytypeid = new immutable
MyTypeInfoImpl_FunctionPointer!T;
else static if(is(T == class))
immutable MyTypeInfo mytypeid = new immutable
MyTypeInfoImpl_Class!T;
else {
//pragma(msg, "generic " ~ T.stringof);
immutable MyTypeInfo mytypeid = new immutable MyTypeInfoImpl!T;
}
}
struct MyVariant {
this(T)(T t) {
opAssign(t);
}
void opAssign(T)(T t) {
storedType = mytypeid!T;
// I know this impl sux but std.variant already solved it so
not the point here.
union SD {
StoredData sd;
T b;
}
SD sd;
sd.b = t;
storedData = sd.sd;
}
T get(T)() {
if(storedType is null)
static if(is(T : typeof(null)))
return null;
else
throw new Exception("null cannot become " ~ T.stringof);
if(storedType.implicitlyConvertsTo(mytypeid!(T))) {
union SD {
StoredData sd;
T b;
}
SD sd;
sd.sd = storedData;
return sd.b;
} else
throw new Exception(storedType.toiString() ~ " cannot become "
~ T.stringof);
}
Rebindable!(immutable(MyTypeInfo)) storedType;
// std.variant already has a better impl of this
struct StoredData {
void* b1;
void* b2;
}
StoredData storedData;
}
class A {}
class B : A {}
import std.stdio;
A func() { writeln("func A"); return null; }
B func2() { writeln("func2 B"); return null; }
void main() {
MyVariant v = &func2;
auto test = &func2;
typeof(&func) test2 = test;
A function() a = v.get!(typeof(&func));
a();
test2();
}
----------------
I understand that's a decent chunk of code and it is very
incomplete - it is basically the minimum to achieve the
challenge. And I think I missed up the contravariant parameter
implementation there, I always forget the exact rule without
looking it up. But nevertheless that's an implementation bug, not
a language barrier.
I hope it shows that this isn't hopeless with today's D. All that
compile time knowledge we need *is* available to Variant itself,
it just needs to dig a little deeper to put it together.... and
then reimplement the compiler's rules, hopefully correctly.
Of course it would be better if we could just reuse dmd's
implementation! But *that* I don't think is possible because
Variant crosses the runtime barrier.... even with a type function
I think it'd need a *pointer* to a typefunction which again hits
that RT/CT barrier.
More information about the Digitalmars-d
mailing list