Hacker News new | ask | show | jobs
by ernst_klim 3351 days ago
> multiple dispatch

> "I don't want to implement the visitor pattern"

Both multiple dispatch and visitor bring type unsafe dynamism and possible runtime errors. Pattern matching and algebraic types fit well into static type system and are much more safe.

1 comments

Why does multiple dispatch bring type unsafe dynamism or runtime errors? Or do you just mean C#'s implementation of multiple dispatch?
Yep, there is no way to check the exhaustiveness in the compile time. Say you have an Expression parent class and a list of child classes: say Add, Const, Var, and a corresponding visitor. Add new child class, and the visitor would be non-exhaustive. Multiple dispatch is not very different in that sense.

I think that Alan Key spoke about dynamic nature of OOP, that's one of the examples. Even in the statically typed environment OOP demonstrates its dynamic nature.

I'm lost here. Either people didn't read the article and are just relying on preconceived notions about what stuff can and can't happen with multiple dispatch, or I am missing something blindingly obvious. Anyway, consider this example:

    class Expression {}
    class Add : Expression {}
    class Const : Expression {}
    class Var : Expression {}

    void FSpecialization(Expression e1, Expression e2) { ... }
    void FSpecialization(Const c, Var v) { ... }

    ...

    void F(Expression e1, Expression e2)
    {
        FSpecialization(e1 as dynamic, e2 as dynamic);
    }
How is this not exhausted? Any new sub class of expression will be routed through to the first FSpecialization. Same as it would with a

    _ -> default_f
in algebraic pattern matching.
It's not exhaustive, because you're only dealing with the Const+Var case. You ignore the other 5 cases. Having a default case doesn't suddenly make it exhaustive, it's just an explicit acknowledgement that you can't.

Because what happens if you introduce a new expression, but not realize this means the implementation of F should be updated?

Regarding algebraic pattern matching, you can let Haskell check exhaustiveness and then drop the default case. The compiler will then warn you if you forgot one.

To give you an example, in our codebase we have an enum that is used 3068 times in one solution. A very similar one (you would need domain knowledge to understand the difference) is used 2985 times. Both are not defined in that solution by the way.

It's not out of the question they will receive a new enum member down the line. It would be very useful if the compiler was able to tell you a switch that uses either enum is no longer exhaustive.

If you could exhaustively switch on an enum, you could skip the default: case and have the compiler enforce you covered all cases. (I know, the C# enum==int would make that impossible.)

>How is this not exhausted?

If you would remove FSpecialization(Expression e1, Expression e2) a.k.a wildcard pattern, compiler would not warn you about your dispatching is not exhaustive. And if you'd add another type of expression, all your dispatchers and visitors without wildcard would become non exhaustive, staying valid code from a compiler perspective at the same time.

I'm sorry would you mind going into a bit more depth about that example? It's not obvious to me why the compiler can't figure out the the visitor is no longer exhaustive.
Visitor is just a collection of functions for all type's subtypes. The problem is that visitor knows nothing about class' subtypes, just like the base class knows nothing about it's children, so you have to add visit cases manually for each child class. The only way to detect that visitor is non exhaustive is to apply it to the child class it has no visit function for.
I think both are not exhaustive (or don't have exhaustiveness enforced by the compiler).