logoalt Hacker News

incrudible01/23/20250 repliesview on HN

> I also prefer pattern matching over dynamic dispatch. But I maintain that conjuring up attributes out of thin air (which is 100% what you're doing when your function accepts one type, and then inside, conjures up another, that has additional attributes from the input!) is insane.

Does it though? In one case, you have:

    type S = A|B
    func calculate(x:S):number
In the other, you have:

    interface S = {calculate:() => number}
    A::calculate():number
    B::calculate():number
but also implicitly the possible:

    Z::calculate():number
The latter case is more flexible, but also harder to reason about. Whoever calls S::calculate can never be quite sure what's in the bag.

> For one, it's a huge footgun for when you add another type that inherits - the compiler will NOT force you to add new branches to cover that case, even though you may want it to.

Lack of exhaustiveness checks is a problem many languages have, and if that's the case for you, that's an argument for preferring methods.

> it's actually n functions, so ease of grokking and maintainability goes out the window

...but that's exactly what interface methods are: N functions. That's what your problem is. You can't get around it. Moreover, if most of your types do not have a distinct implementation for that method, you will still need to define them all. This is where both the "super-function" and inheritance (or traits) can save you quite a bit of code.