digitalmars.D.learn - static switch/pattern matching
- John (29/29) Jun 25 2016 Writing a long series of "static if ... else" statements can be
- Lodovico Giaretta (11/42) Jun 25 2016 Instead of passing functions to match!, pass pairs of arguments,
- Lodovico Giaretta (17/27) Jun 25 2016 Of course I meant:
- John (27/48) Jun 25 2016 Thanks for the help, both. This appeared to work, until I
- Lodovico Giaretta (12/37) Jun 25 2016 If you want this to work, you need your lambdas to take the
- Lodovico Giaretta (27/38) Jun 25 2016 Something like this:
- John (3/8) Jun 25 2016 Thanks.
- ketmar (48/49) Jun 25 2016 q&d hack:
- ketmar (1/1) Jun 25 2016 also, there is a subtle bug in matcher. sorry. ;-)
Writing a long series of "static if ... else" statements can be
tedious and I'm prone to leaving out the crucial "static" after
"else", so I was wondered if it was possible to write a template
that would resemble the switch statement, but for types.
Closest I came up to was this:
void match(T, Fs...)() {
foreach (F; Fs) {
static if (isFunctionPointer!F) {
alias Ps = Parameters!F;
static if (Ps.length == 1) {
static if (is(Ps[0] == T)) F(Ps[0].init);
}
}
}
}
void test(T)(T t) {
match!(T,
(int _) => writeln("Matched int"),
(string _) => writeln("Matched string")
);
}
But that's pretty limited and I'd like to be able to match on
whether a type derives from T as well. I just can't figure it out.
Something like this would be ideal...
match!(T,
int => writeln("Matched int"),
is(T : SomeObject) => writeln("Derives from SomeObject")
);
Anyone able to improve on it?
Jun 25 2016
On Saturday, 25 June 2016 at 08:46:05 UTC, John wrote:
Writing a long series of "static if ... else" statements can be
tedious and I'm prone to leaving out the crucial "static" after
"else", so I was wondered if it was possible to write a
template that would resemble the switch statement, but for
types.
Closest I came up to was this:
void match(T, Fs...)() {
foreach (F; Fs) {
static if (isFunctionPointer!F) {
alias Ps = Parameters!F;
static if (Ps.length == 1) {
static if (is(Ps[0] == T)) F(Ps[0].init);
}
}
}
}
void test(T)(T t) {
match!(T,
(int _) => writeln("Matched int"),
(string _) => writeln("Matched string")
);
}
But that's pretty limited and I'd like to be able to match on
whether a type derives from T as well. I just can't figure it
out.
Something like this would be ideal...
match!(T,
int => writeln("Matched int"),
is(T : SomeObject) => writeln("Derives from SomeObject")
);
Anyone able to improve on it?
Instead of passing functions to match!, pass pairs of arguments,
like this:
match!(T,
int, writeln("Matched int"),
is(T : SomeObject), writeln("Derives from SomeObject");
);
Now, in the implementation, foreach pair of arguments, if the
first member is a type that matches your target, perform that
branch; otherwise, if the first member is a boolean value, and it
is true, perform the branch.
Jun 25 2016
On Saturday, 25 June 2016 at 09:07:19 UTC, Lodovico Giaretta
wrote:
Instead of passing functions to match!, pass pairs of
arguments, like this:
match!(T,
int, writeln("Matched int"),
is(T : SomeObject), writeln("Derives from SomeObject");
);
Now, in the implementation, foreach pair of arguments, if the
first member is a type that matches your target, perform that
branch; otherwise, if the first member is a boolean value, and
it is true, perform the branch.
Of course I meant:
match!(T,
int, () {writeln("Matched int");},
is(T : SomeObject), () {writeln("Derives from
SomeObject");}
);
You could probably even match on the actual value (instead of its
type) and pass it (correctly casted) to the functions:
match!(t,
int, (int t) {writeln("Matched int ", t);},
is(T : SomeObject), (SomeObject t) {writeln(t, " derives
from SomeObject");}
);
I don't have time to implement it now, but I think it's not too
difficult.
Jun 25 2016
On Saturday, 25 June 2016 at 09:12:12 UTC, Lodovico Giaretta wrote:On Saturday, 25 June 2016 at 09:07:19 UTC, Lodovico Giaretta wrote:Thanks for the help, both. This appeared to work, until I realised the lambda isn't static: void match(T, cases...)() { static if (cases.length == 1) cases[0](); else static if (cases.length > 2) { static if (is(typeof(cases[0]) == bool)) { static if (cases[0]) cases[1](); else match!(T, cases[2 .. $]); } else static if (is(T == cases[0])) cases[1](); else match!(T, cases[2 .. $]); } } void test(T)(T value) { int i; string s; match!(T, int, () => i = value, string, () => s = value ); } test(1); test("A string"); The compiler complains about not being able convert an int to a string and vice versa.Instead of passing functions to match!, pass pairs of arguments, like this: match!(T, int, writeln("Matched int"), is(T : SomeObject), writeln("Derives from SomeObject"); ); Now, in the implementation, foreach pair of arguments, if the first member is a type that matches your target, perform that branch; otherwise, if the first member is a boolean value, and it is true, perform the branch.Of course I meant: match!(T, int, () {writeln("Matched int");}, is(T : SomeObject), () {writeln("Derives from SomeObject");} );
Jun 25 2016
On Saturday, 25 June 2016 at 10:39:09 UTC, John wrote:
Thanks for the help, both. This appeared to work, until I
realised the lambda isn't static:
void match(T, cases...)() {
static if (cases.length == 1) cases[0]();
else static if (cases.length > 2) {
static if (is(typeof(cases[0]) == bool)) {
static if (cases[0]) cases[1]();
else match!(T, cases[2 .. $]);
}
else static if (is(T == cases[0])) cases[1]();
else match!(T, cases[2 .. $]);
}
}
void test(T)(T value) {
int i;
string s;
match!(T,
int, () => i = value,
string, () => s = value
);
}
test(1);
test("A string");
The compiler complains about not being able convert an int to a
string and vice versa.
If you want this to work, you need your lambdas to take the
casted value as a parameter:
void test(T)(T value) {
int i;
string s;
match!(value,
int, (val) => i = val,
string, (val) => s = val
);
}
And of course you need to modify match! for this to work.
Jun 25 2016
On Saturday, 25 June 2016 at 12:30:22 UTC, Lodovico Giaretta
wrote:
If you want this to work, you need your lambdas to take the
casted value as a parameter:
void test(T)(T value) {
int i;
string s;
match!(value,
int, (val) => i = val,
string, (val) => s = val
);
}
And of course you need to modify match! for this to work.
Something like this:
void match(alias t, cases...)() {
static if (cases.length == 1) cases[0]();
else static if (cases.length > 2) {
static if (is(typeof(cases[0]) == bool)) {
static if (cases[0]) cases[1](t);
else match!(t, cases[2 .. $]);
}
else static if (is(typeof(t) == cases[0])) cases[1](t);
else match!(t, cases[2 .. $]);
}
}
void test(T)(T value) {
int i;
string s;
match!(value,
int, (val) => i = val,
string, (val) => s = val
);
}
void main()
{
test(1);
test("A string");
}
Jun 25 2016
On Saturday, 25 June 2016 at 12:35:39 UTC, Lodovico Giaretta wrote:On Saturday, 25 June 2016 at 12:30:22 UTC, Lodovico Giaretta wrote:Thanks.If you want this to work, you need your lambdas to take the casted value as a parameter:
Jun 25 2016
On Saturday, 25 June 2016 at 08:46:05 UTC, John wrote:Anyone able to improve on it?q&d hack: template tyma(T, Cases...) { import std.traits; template GetFunc(size_t idx) { static if (idx >= Cases.length) { static assert(0, "no delegate for match"); } else static if (isCallable!(Cases[idx])) { enum GetFunc = Cases[idx]; } else { enum GetFunc = GetFunc!(idx+1); } } template Matcher(size_t idx) { //pragma(msg, "T=", T, "; idx=", idx, "; Cases[idx]=", Cases[idx], "; is=", is(typeof(T) == Cases[idx])); static if (idx >= Cases.length) { static assert(0, "no match, consider adding `void` branch"); } else static if (isCallable!(Cases[idx])) { enum Matcher = Matcher!(idx+1); } else static if (is(Cases[idx] == void)) { enum Matcher = GetFunc!(idx+1); } else static if (is(typeof(Cases[idx]) == string)) { mixin("static if (is(T:"~Cases[idx]~")) enum Matcher = GetFunc!(idx+1); else enum Matcher = Matcher!(idx+1);"); } else static if (is(typeof(Cases[idx]))) { static assert(0, "unexpected something in cases: "~Cases[idx].stringof); } else static if (is(T == Cases[idx])) { enum Matcher = GetFunc!(idx+1); } else { enum Matcher = Matcher!(idx+1); } } enum tyma = Matcher!0; } void main () { import std.stdio; auto res = tyma!(int, string, () => "string", "long", () => "integral", void, () => "anything", )(); writeln(res); } note that you should separate type names from labdas with "," instead of doing `int => "integral`, and have to add `()` at the end to actually call the delegate.
Jun 25 2016
also, there is a subtle bug in matcher. sorry. ;-)
Jun 25 2016









John <johnch_atms hotmail.com> 