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