https://people.csail.mit.edu/dnj/teaching/6898/papers/wadler... (page 3)
https://people.csail.mit.edu/dnj/teaching/6898/papers/wadler... (page 3)
Encapsulation — If you understand by OOP the encapsulation of data, then no, type classes have no such thing.
Inheritance — type classes don't have inheritance. What they have are constraints (e.g. you can implement typeclass X for type T if a typeclass implementation Y also exists for T). So you can say for example that MonadError can be implemented only for types already implementing Monad. That's not inheritance, but composition.
Most importantly perhaps and something that doesn't clearly follow from the above is that OOP as implemented in popular languages, including C++, gives you subtyping, whereas type classes do not.
This has important ramifications. For example downcasting isn't possible. And you no longer have the "liskov substitution principle". And subtyping is actually incompatible with Hindley-Milner type inference.
Also in actual usage the differences can be quite big — for example, if you view type classes as "restrictions" on what a type can do (like OOP interfaces are), you no longer need to add these restrictions on the types themselves, but on the operations, where you actually need them, while the data structure definition can remain free of such restrictions.
This is called "constrained parametric polymorphism" btw. Which can't be achieved in most OOP languages that I know of, except for Scala and that's because Scala has "implicit parameters" which are equivalent to Haskell's type classes.
At the end of the day, what a type class gives you is a way to transform a type name into a meaningful value. Imagine having a function like ...
f: T => A
But the T param is a type name. That's what type classes give you, whereas OOP does not. data Object cls a where
Object :: cls a => a -> Object cls a
Now, the class `cls a` is carried around with the value `a`. You can even go one step further and reify the vtable by itself. data Dict c where
Dict :: c => Dict
which you can't really do in standard C++.The more common terminology is ad-hoc polymorphism AFAIK.
data Nested a = Nest a (Nested [a]) | Done deriving Show
build :: Int -> a -> Nested a
build 0 a = Done
build i a = Nest a $ build (i- 1) [a]
main = do
i <- readLn
print (build i i)
if you build a value of Nested at runtime then it is impossible to do static dispatch and the dictionary has to be constructed at runtime as well.Sorry that I mistyped the example at first. If you run this version:
> main
23
Nest 23 (Nest [23] (Nest [[23]] (Nest [[[23]]] (Nest [[[[23]]]] (Nest [[[[[23]]]]] (Nest [[[[[[23]]]]]] (Nest [[[[[[[23]]]]]]] (Nest [[[[[[[[23]]]]]]]] (Nest [[[[[[[[[23]]]]]]]]] (Nest [[[[[[[[[[23]]]]]]]]]] (Nest [[[[[[[[[[[23]]]]]]]]]]] (Nest [[[[[[[[[[[[23]]]]]]]]]]]] (Nest [[[[[[[[[[[[[23]]]]]]]]]]]]] (Nest [[[[[[[[[[[[[[23]]]]]]]]]]]]]] (Nest [[[[[[[[[[[[[[[23]]]]]]]]]]]]]]] (Nest [[[[[[[[[[[[[[[[23]]]]]]]]]]]]]]]] (Nest [[[[[[[[[[[[[[[[[23]]]]]]]]]]]]]]]]] (Nest [[[[[[[[[[[[[[[[[[23]]]]]]]]]]]]]]]]]] (Nest [[[[[[[[[[[[[[[[[[[23]]]]]]]]]]]]]]]]]]] (Nest [[[[[[[[[[[[[[[[[[[[23]]]]]]]]]]]]]]]]]]]] (Nest [[[[[[[[[[[[[[[[[[[[[23]]]]]]]]]]]]]]]]]]]]] (Nest [[[[[[[[[[[[[[[[[[[[[[23]]]]]]]]]]]]]]]]]]]]]] Done))))))))))))))))))))))
The innermost Nested has type `Nest [[[[[[[[[[[[[[[[[[[[[[Int]]]]]]]]]]]]]]]]]]]]]]` which wasn't known at compile time so the show instance has to be constructed at runtime. This is called polymorphic recursion.I'd say the difference is minor to non-existent.
There is no "isInstanceOf" check in Haskell.
C++ can do that with template specialization.
This might be nitpicking but vtables in C++ are created per type with instances having a pointer to it.
Although the language does not offer a builtin syntax for it, in C++ is also common to attach the 'vtable' to the 'handle' (a smart pointer or deep copying envelope), in the case of type erased wrappers (std::function, std::any, etc).
I enjoyed this post since it's kind of a mirror of this chapter, which was one of the hardest for us to figure out what to put in it.
1: https://doc.rust-lang.org/book/second-edition/ch17-00-oop.ht...
The trick here is defining "object-oriented". In modern object-oriented languages, I usually assume that involves encapsulation of state inside of boxes called objects. In that sense, type classes are quite different.
Methods on objects generally mutate or provide views into the contents of the box, while methods in type classes are just regular functions. Critically, there is no inner mutation or privileged view into data via type classes.
> They have things like properties and multiple inheritance
How do they have properties? Also, they don't have multiple inheritance - a type can only ever have one instance of any given typeclass.
> Even Wadler has a section leading with "Object-oriented programming."
I read Wadler's paragraph on object-oriented programming (bottom of page 3) more as a highlight of the fundamental _difference_ between type classes and object-oriented classes. The only similarity between typeclasses and classes are that they are the mechanisms by which runtime dispatch is achieved.
We should call programming with classes and inheritance. COP, as in Class Oriented Programming. COP is especially appropriate because if you don't follow the type rules, you go to compiler jail.
That is the Alan Kay definition. I'm not sure I'd claim that is the colloquial, universal understanding of OOP apart from the HN crowd, though. (ask a LOB programmer what OOP is and observe what they say).
I rather like the Alan Kay definition myself, but that is rather like having a Many Worlds understanding of quantum mechanics; Entirely consistent and arguably "correct", but not universally shared among practitioners.
We should refine our language (e.g. COP), but it would be revisionist to ignore what the 90's and 00's understanding of the definition would be.
I think it would be even more of a stretch to say that prototypal languages (javascript) are more OOP than non-prototypal languages, unless of course one is being a bit reductionist.
I'm not sure what your ordering relation is here, but if it has something to do with how "purely object-oriented" a language is, then, sure, JavaScript is more OOP than C++ and Java since classes are not first-class in the latter.
But Smalltalk, which is class-based, is even more OOP than JavaScript since in Smalltalk, constructor calls are simply regular method calls (unlike JS which has a special "new" keyword) and there are no primitive objects (unlike JS, which distinguishes between primitive types and their boxed forms, though it tries to obscure that fact).
> COP is especially appropriate because if you don't follow the type rules, you go to compiler jail.
Smalltalk, Ruby, and Python are all class-based OOP languages but do not have static type systems with lots of compiler errors.
This becomes even more apparent when using an actor-model language like Erlang. The types of "design patterns" you use focus much more around the communication and coordination you might find in real, physical systems. Consequently I find it easier to model "real" systems using actors than standard objects in a language like Java.
I also appreciate how the Pony language decomposes the notion of "objects" into actors that have their own process and capabilities (standard objects). It shows that their is a distinct role for both types of objects, and it helps when decomposing entities in a system.
If classes and inheritance aren't a form of OOP, then what are they?
Also, Smalltalk had classes and inheritance, whatever Alan Kay had to say on the matter.
Type-classes do allow interesting polymorphism tricks, that —as Wadler mentioned— allows Haskellers to use some of good parts of OO.
It is with cloud-native that it gets interesting. Cloud-native infrastructure (like k8s) usually replaces the traditional devops tools (like Puppet, Ansible and Salt). Also it brings the notion of "scrappable instances", "blue green deployments" and "stateless compute nodes". This is were it got really interesting, this is were the FP intuitions start carry over. :)
https://wycd.net/posts/2017-04-09-rpcs-for-moving-computatio...
s/not possible/possible, but highly discouraged/