3 ms·
Mirror of my question from Reddit: Don't classes cover a full superset of the functionality of ML-style functors? I have yet to find a functor-dependent constr
by saosebastiao 10y ago
Mirror of my question from Reddit:
Don't classes cover a full superset of the functionality of ML-style functors? I have yet to find a functor-dependent construct that can't be mimicked with them, and I've found plenty of use cases of classes that can't be mimicked (or at least not without hacky workarounds) with functors. And yet it seems common for FP style programmers to lament the lack of functors in F#, but will consider it a codesmell to use classes. Am I missing something here? What does a functor do that is so taboo to do with classes?
- Tarean 10y agoYou might be misunderstanding functors since they are basically tangential to classes. Every generic class `F<T>` where we can lift functions `T -> S` to `F<T> -> F<S>` is a functor. Think of lists as example, they are generic over a type and we can apply functions over the base type by applying them to each element. Functors are a basic building block of category theory. Category theory is basically the essence of composition so using functors leads to easy to reason about and extremely composable code. This also extends to things that build on functors like monads.
- pseudonom- 10y agoML uses "functors" in a different sense than e.g. Haskell.
- saosebastiao 10y agoIn ML a functor is a very different thing from Haskell. It is akin to a higher order module. http://stackoverflow.com/a/16353711 http://stackoverflow.com/a/16353711
- tel 10y agoClasses and functors are still a bit orthogonal. There are tons of things you can do with classes nowadays depending on what language you're working in, but essentially, fundamentally, functors are about types and classes are about value construction. Many type-level things have been ported over to classes (information hiding, parameterization, sometimes even type members) but these are more clearly functor-like ideas. So it doesn't surprise me to think that classes can emulate functors when their functionality is often built by emulating functors while functors can't emulate classes since functors as they're implemented in SML/OCaml assume that value construction will just be done by plain functions (or OCaml's class construct). This gets back the one of the cruxes of the LSP—it's about subtyping whereas many class systems are about subclassing and those two things actually just have different behavior. So perhaps the one thing that functors do---and the thing that if I were a F# programmer I'd guess is the thing that I'd miss---is intentionally not do things that classes do. Functors are a narrower interface which makes it simpler to reason about ("Liberties constraint, constraints liberate").
- rbehrends 10y agoNo. ML functors are about parametric polymorphism, classes are about subtype polymorphism. There are some inherent similarities between the two, but also some differences that are non-trivial to reconcile (such as early vs. late binding). C++ templates come close, but that's because they also are about parametric polymorphism, not classes as such. ML functors are modules that are parameterized by other modules. A common use case is that the parameter module contains several types and the logic for their interaction. Unless you've got sufficiently expressive virtual types or an equivalent mechanism (C++ templates), you won't be able to approximate that with inheritance alone. Scala in some ways approximates the expressive power of ML functors, but at the expense of being hugely more complicated. One of the virtues of ML functors is that they are fairly straightforward, both in terms of use and implementing them in a compiler.
- saosebastiao 10y agoBut classes are capable of both parametric polymorphism AND subtype polymorphism. For example, compare `class FooFunctor[T <: FooSig](arg: T)` to `module FooFunctor (arg: FooSig)`. Aren't those equivalent? What do you mean by late binding vs early binding?
- rbehrends 10y agoFirst, the parametric polymorphism mechanisms in most OO languages are orthogonal to the concept of classes. Second, as I said, try it with multiple types that are interdependent. Once you do that, you go down the rabbit hole of higher-kinded and existential types to make sure that everything lines up properly with the compiler's inference mechanisms. A key difference is that the generics in most OO languages allow you to parameterize only by types. An ML functor makes the parameter a module. Everything you can stuff in a module can be part of the parameter signature, and you can create whatever relationships you need between them. And you can do things such as hash tables with both case-sensitive and case-insensitive string keys that still share the same string type. Finally, in your Scala example, keep in mind that unlike ML functors, it has the usual constructor problem that many simple genericity mechanisms have: constructing instances of the parameter type. In Scala, that means you end up working with CanBuildFrom or implicit arguments; C# and Eiffel have specialized (and ultimately, still less flexible) language constructs to deal with that.