4 ms·
Inheritance can simulate sum-types. You can also simulate sum-types via what OOP people would probably call a visitor pattern: you hand me call-backs for what
by eru 1y ago
Inheritance can simulate sum-types. You can also simulate sum-types via what OOP people would probably call a visitor pattern: you hand me call-backs for what to do in all the different cases, and some trusted piece of code that you implement once for the type, does the case distinction and calls the right call-back. (You can either hand over the call-backs as functions / function pointers, or you can implement them as member methods pure OOP style.)
They are all equivalent in principle, but some of them are a lot more annoying to work with, especially when you want to do a pattern matching over multiple values at the same time, or match on nested patterns.
- Buttons840 1y agoIf I write my happy_or_sad() callback and pass it to the Shape, it would be nice if I could get some exhaustiveness checking, but like the blog says, C++ and other popular languages intentionally left it out.
- eru 1y agoIn what I described, you would need to hand callbacks for all possibilities, because no parameters are optional.
- layer8 1y agoWith the visitor pattern, you have to implement an interface with one method for each choice, so (in statically typed languages) that implicitly does an exhaustiveness check, because if you forget to implement one of the interface methods, instantiating your implementing class won’t compile. One nice thing about the visitor pattern is that it doesn’t have to match the type hierarchy. For example, you could have a visitor interface method that is invoked for blue shapes, even if there is no BlueShape type. Similarly, the same type hierarchy can support multiple visitor interfaces, so that you can perform different case distinctions on the same value. This is something that sum types can’t do.
- eru 1y ago> Similarly, the same type hierarchy can support multiple visitor interfaces, so that you can perform different case distinctions on the same value. This is something that sum types can’t do. Haskell's pattern synonyms should be able to handle this? The problem with the visitor pattern is that it doesn't compose well. So if you want to match on two values at once, or match deeper into a value, that works well for most pattern matching, but is annoying to piece together with the visitor pattern.
- pyrale 1y agoA gard on your pattern match would be enough I guess.
- eru 1y agoIt depends on what you are trying to do. Eg guards can really replace or-patterns.
- layer8 1y agoI’m not really familiar with Haskell’s pattern synonyms, but maybe yes. What the visitor pattern gives you is that in principle the implementation structure can be orthogonal to the visitor case distinction. You can have objects encapsulate (and possibly hide) one structure while exposing different structural views via visitor. This also allows to re-arrange the implementation of the alternatives without breaking client code. In functional languages, you’d probably rather have functions converting the source value to a different (sum) type to perform the case distinction on. I agree that syntactic sugar for visitor ”matching” would be nice, and it’s something a language could add. The visitor pattern itself doesn’t prevent adding such syntax.
- greener_grass 1y agoThe designer of the type must manually ensure exhaustive checking but then consumers will be forced to do exhaustive matching. It's not that bad in practice.
- pyrale 1y ago> You can also simulate sum-types via what OOP people would probably call a visitor pattern You can also simulate it by using if/else everywhere. At this point, that means you have zero language feature supporting the use case, and type safety is up to the developers implementing patterns correctly everywhere.
- pjmlp 1y agoThe way it goes is that sum types are closed for extension, while enjoying the confort of pattern matching, whereas OOP approach is open for extension, with the associated boilerplate in the visitor logic. In languages of ML linage, we can combine both approaches with a mix of sum types and functors/type classes.
- eru 1y ago> [...] whereas OOP approach is open for extension, with the associated boilerplate in the visitor logic. Yes, by default. But doesn't eg Java let you mark classes as final or something like that? > In languages of ML linage, we can combine both approaches with a mix of sum types and functors/type classes. I think Rust allows something similar, but I'm not sure whether you'd call it 'ML linage'?
- pjmlp 1y agoYes, you can mark classes final as closed for extension, however Java follows the wisdom at the time that by default classes are open for extension. Additionally, modern Java has sum types modeled on the same way as Scala. Rust is to some extent inspired by ML languages, hence why several OCaml and Haskell refugees also hang around Rust.