Thread overview | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
June 25, 2016 static switch/pattern matching | ||||
---|---|---|---|---|
| ||||
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? |
June 25, 2016 Re: static switch/pattern matching | ||||
---|---|---|---|---|
| ||||
Posted in reply to John | 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.
|
June 25, 2016 Re: static switch/pattern matching | ||||
---|---|---|---|---|
| ||||
Posted in reply to John | 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.
|
June 25, 2016 Re: static switch/pattern matching | ||||
---|---|---|---|---|
| ||||
Posted in reply to ketmar | also, there is a subtle bug in matcher. sorry. ;-) |
June 25, 2016 Re: static switch/pattern matching | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lodovico Giaretta | 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.
|
June 25, 2016 Re: static switch/pattern matching | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lodovico Giaretta | 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:
>>
>> 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");}
> );
>
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.
|
June 25, 2016 Re: static switch/pattern matching | ||||
---|---|---|---|---|
| ||||
Posted in reply to John | 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.
|
June 25, 2016 Re: static switch/pattern matching | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lodovico Giaretta | 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");
}
|
June 25, 2016 Re: static switch/pattern matching | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lodovico Giaretta | 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:
>> If you want this to work, you need your lambdas to take the casted value as a parameter:
>>
Thanks.
|
Copyright © 1999-2021 by the D Language Foundation