News for hackers. Stuff that matters.
Re:Codata in action, or how to connect FP and OOP by tsimionescu on Monday August 10, 2020 @03:21AM (#24106503)
I don't think multiple dispatch fixes this problem in any way.

The idea here is that you can have an opaque interface. Then, adding new implementations of that interface is very easy. But, if you want to extend the interface, you need to modify all of the existing implementations. Multiple dispatch suffers of this problem just as much as single dispatch.

In the other case, you have a transparent interface - anyone can build code based on your data type. But, if you add more cases to your data type, anyone who had functionality built on it needs to take into account the new cases.

I think that this is in inherently an unavoidable problem. You can of course expose both kinds of constructs in your language, but you can't make interface implementation easily modifiable, and you can't make sum types easily extensible. The programmer ultimately has to choose the one that they believe will best capture the domain and its probable evolution.

What multiple dispatch can do is take some cases where sum types are obviously preferable to single dispatch (operations on related objects) and apply the interface abstraction to those cases as well (without reaching for horrible solutions like the visitor pattern).

Edit: thinking a bit more about the article, what they are showing is that you can essentially use the visitor pattern (and multiple dispatch) to implement GADTs instead of using them for regular multiple dispatch with inheritance. Basically, you can use them bring completely disparate types under the same umbrella, just like a GADT. I'm not sure that this still allows subtyping of that GADT as if it were a regular class.

Re:Codata in action, or how to connect FP and OOP by codr7 on Monday August 10, 2020 @07:53AM (#24108270)
There is no need to modify existing implementations when extending an interface using multiple dispatch.

You would need to add the required implementations, but there's really no way around that since the logic has to go somewhere.

And as soon as you start actually dispatching on multiple arguments, using sum types to solve the same prolem quickly turns into spaghetti.

Re:Codata in action, or how to connect FP and OOP by tsimionescu on Monday August 10, 2020 @08:30AM (#24108530)
> There is no need to modify existing implementations when extending an interface using multiple dispatch.

Sure there is. If I have a function that takes a Foo and a Bar, and it calls a generic method on them, the generic method must have methods that cover the real subtypes of Foo and Bar. If someone had created subtypes of Foo and Bar and defined methods on all the generic methods that they knew about, and now you create a new generic method, you have absolutely broken the interface just as much as adding a new method to Iterable in Java.

Re:Codata in action, or how to connect FP and OOP by codr7 on Monday August 10, 2020 @10:01AM (#24109310)
It must have enough methods to get the behavior you want, that doesn't always mean all subtypes.

Assuming you're running a version of Java that supports default implementations, which may often be used to avoid breaking the interface.

Sum types are a different story, as you would have to dive into existing code.

Re:Codata in action, or how to connect FP and OOP by contravariant on Monday August 10, 2020 @05:17AM (#24107168)
> I'm not sure that this still allows subtyping of that GADT as if it were a regular class.

It does, more or less, since any subtype will have to implement the "accept" method for the visitor, which has been defined to only 'accept' the values that can be represented by the GADT. Any subtype will need to define how its possible values map to that of the supertype.

Re:Codata in action, or how to connect FP and OOP by hlasdjlfhalwjk on Monday August 10, 2020 @05:18AM (#24107171)
Some Rust libraries use a trick to make sum types extensible without introducing breaking changes when a new invariant is added. There is a private/undocumented invariant in the type so every pattern match over the type must have a default/catch-all/wildcard branch (e.g. it is not possible to do an exhaustive match by naming all existing variants). By having a default branch, the match is always exhaustive and there will be no error if a new variant is added. For fields in a sum type something similar can be done: an undocumented, hidden field that cannot be used when the variant is destructed. Therefore it can be only partially destructed and in this case it doesn't matter how many other fields there are in the type variant.

Here is an example: https://github.com/rust-lang/regex/blob/master/regex-syntax/...

Re:Codata in action, or how to connect FP and OOP by ori_b on Monday August 10, 2020 @07:23AM (#24108028)
Forcing a default case is a big step back towards dynamic typing.
Re:Codata in action, or how to connect FP and OOP by heavenlyblue on Monday August 10, 2020 @09:45AM (#24109142)
“Hey - we have a solution to your type problem; we just disabled type checking. Genious”