TLDR: Is there a way make D's SumType play nice with opCmp while maintaining its functionality?
Context
I'm writing a program for which D's native SumType works almost completely. However, I would like to be able to do the following:
alias Foo = SumType!(int, string);
Foo x = 3;
Foo y = 5;
writeln(max(x, y));
However, since no ordering is natively defined for SumType, I receive the following error:
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\comparison.d(1644): Error: static assert: "Invalid arguments: Cannot compare types SumType!(int, string) and SumType!(int, string) for ordering."
mwe.d(11): instantiated from here: `max!(SumType!(int, string), SumType!(int, string))`
I was able to remedy this specific issue using the following method:
import std.stdio : writeln;
import std.exception : assertThrown;
import std.algorithm.comparison : max;
import core.exception : AssertError;
import std.sumtype;
struct Foo {
SumType!(int, string) value;
this(T)(T v) {
value = v;
}
ref Atom opAssign(T)(T rhs) {
value = rhs;
return this;
}
int opCmp(Foo other) {
return match!(
(a, b) => a < b ? -1 : a == b ? 0 : 1,
(_1, _2) => assert(0, "Cannot match")
)(value, other.value);
}
}
void main() {
Foo x = 3;
Foo y = 7;
Foo z = "asdf";
assert(x < y); // comparing ints works correctly
assertThrown!AssertError(x < z); // cannot compare int and string
assert(max(x, y) == y); // D's max works
}
The Problem
While I can now use x.value.match!(...) where I used to use x.match!(...), I would like to still be able to call .match! directly on x, and also use match!(...)(x, y) instead of match!(...)(x.value, y.value). I do not like the idea of inserting hundreds of .value throughout my code just to make certain functions like max work, and would prefer if there were a more elegant solution. I tried tinkering around with defining a custom opDispatch using mixins but I couldn't get that to play nicely with the existing SumType:
struct Foo {
SumType!(int, string) value;
this(T)(T v) {
value = v;
}
ref Atom opAssign(T)(T rhs) {
value = rhs;
return this;
}
int opCmp(Foo other) {
return match!(
(a, b) => a < b ? -1 : a == b ? 0 : 1,
(_1, _2) => assert(0, "Cannot match")
)(value, other.value);
}
auto opDispatch(string name, T...)(T vals) {
return mixin("value." ~ name)(vals);
}
}
void main() {
Foo y = 7;
y.match!(
(int intValue) => writeln("Received an integer"),
(string strValue) => writeln("Received a string")
);
}
And I am unable to decode the error which results:
mwe.d(38): Error: none of the overloads of template `std.sumtype.match!(function (int intValue) @safe
{
writeln("Received an integer");
return ;
}
, function (string strValue) @safe
{
writeln("Received a string");
return ;
}
).match` are callable using argument types `!()(Foo)`
C:\D\dmd2\windows\bin\..\..\src\phobos\std\sumtype.d(1659): Candidate is: `match(SumTypes...)(auto ref SumTypes args)`
with `SumTypes = (Foo)`
must satisfy the following constraint:
` allSatisfy!(isSumType, SumTypes)`
Beyond that I am out of ideas as to how to find a less clunky solution.