Pattern matching in D
retard
re at tard.com.invalid
Mon Mar 8 04:22:13 PST 2010
Mon, 08 Mar 2010 03:35:56 -0800, Walter Bright wrote:
> retard wrote:
>>> I've thought more than once about adding that, but it just seems
>>> pointless. I've never run into a use case for it. If you do run into
>>> one,
>>>
>>> if (ar == [1,2,3]) ...
>>> else if (ar == [1,2,4]) ...
>>> else if (ar == [1,3,2]) ...
>>>
>>> will work just fine.
>>
>> The same can be said about matching integer values:
>>
>>> if (ar == 1) ...
>>> else if (ar == 2) ...
>>> else if (ar == 3) ...
>
> I don't agree, because switching on integer values is commonplace, and
> switching on array literals pretty much never happens.
>
> (Note: switching on floating point values is worse than useless, as
> floating point computations rarely produce exact results, and supporting
> such a capability will give a false sense that it should work.)
>
>
>> I guess the point is just to unify many kinds of cases.
>>
>> class Foo
>> class Bar : Foo { void doBarMethod() {} } class Bar2 : Foo { void
>> doBar2Method() {} }
>>
>> if (cast(Bar)foo)
>> (cast(Bar)foo).doBarMethod();
>> else if (cast(Bar2)foo)
>> (cast(Bar2)foo).doBar2Method();
>> else
>> writefln("Oh no!")
>
>
> I have to say, if you find code written that way, you're doing OOP
> wrong.
The "correct" solution, multimethods or the visitor pattern, is sometimes
very expensive. They become very verbose when compared to simple pattern
matching:
>> foo match {
>> case b: Bar => b.doBarMethod
>> case b: Bar2 => b.doBar2Method
>> case _ => println("Oh no!")
>> }
The OOP argument is false here. Sometimes OOP isn't the best way to
describe declarative data. It surely allows you to implement this, but
the cost is a much larger verbosity.
The data type might be just a simple tagged union (in which case powerful
optimizations can be used). Instead of writing
> enum node_type { sum_node, mul_node, ... }
>
> struct tree {
> node_type type;
>
> union {
> // bla bla
> }
> }
you can automatically eliminate the boilerplate code with this higher
level construct. It's also much safer - unions are known to be a security
loophole when used carelessly. Also with instanceof/cast() you may need
to cast twice unlike in the match expression above.
>>
>> (1, (1,2)) match {
>> case (1, (1, _)) => println("nice tuple") case _ =>
>> }
>>
>> def main(args: Array[Byte]) =
>> args(0) match {
>> case "-help" => println("Usage: ...") case "-moo" => println("moo")
>> }
>
> D already supports string switches.
I know that. The idea was to show how general pattern matching unifies
the instanceof construct, integer switches, string switches and various
kinds of algebraic data type manipulation such as tree/graph rewriting.
It's a much more generalized construct in all possible ways - and very
intuitive to use. Some of the beauty is lost when you read the example
written in Scala, but e.g. in Haskell the pattern matching closely
reflects the structure of the algebraic data type.
More information about the Digitalmars-d
mailing list