3 ms·
> Data structure and functions should not be bound together Maybe not always. I suspect, based on Kay's writings, that Smalltalk's "everything is an object" th
by mumblemumble 6y ago
> Data structure and functions should not be bound together
Maybe not always. I suspect, based on Kay's writings, that Smalltalk's "everything is an object" thing was more about experimentation. They were trying to push one simple idea as far as possible, in order to see just how far they could push it. It turns out you can go pretty far. That doesn't necessarily mean you need to.
That said, the most popular language that shies away from "everything is an object" - Java - does so in a way that doesn't work well. Primitives are not objects, sure, but primitives then integrate poorly with the rest of the language (especially since Java 5), and more complex data structures must be objects, even if you don't want them to be.
Ironically, my favorite language for showing the nice things you can get by relaxing the "everything is an object" ethos, F#, actually does make everything into an object. (It has to, because .NET.) But it doesn't force you to think about them as if they were objects when you don't want to. So the mental space you live in when you're using the language feels primarily functional.
But sometimes objects are useful. The key distinction here - and the thing that Armstrong seems to miss in this essay - is that methods aren't just functions that have been glued to some data. Technically I suppose they are - that's certainly how it works when you roll your own OOP in a language like C - but it turns out the whole is more than the sum of its parts. Because you get a really useful thing that isn't so convenient to do when you keep your functions and your data separate: dynamic dispatch.
Dynamic dispatch - not encapsulation - is the killer feature of objects. Late function binding allows you to operate over heterogeneous inputs where the only thing you want to enforce is that they obey a protocol, without having to do mess of explicit conditional branching or having to have all the details pinned down at compile time. Functional languages without any OOP facilities run into convenience problems here. Typeclasses cover some of the same use cases, as well as others that objects and interfaces don't handle very well at all, but they're statically bound, so they can be somewhat less flexible. It's often stated that design patterns are just a way of making up for a missing feature in your language. Well, the command pattern is what you do when your language doesn't support OOP.
(For another example of where Armstrong was clearly operating under some misapprehensions when he wrote this, look at the truly bizarre statement he makes in the last paragraph of section 3.)
- hderms 6y agoYeah an example of typeclasses being less flexible to me is if what you really want is OOP polymorphism for something like being able to choose what code to run based on configuration. Given that you can add objects to your class path at runtime, you can clearly get some extreme flexibility out of a system without clear parallels in typeclass based polymorphism. Obviously this isn't something you'd want to do all the time, but it's an example of something I think about periodically when comparing the two approaches. With typeclasses, polymorphic behavior is driven strictly by the type, and one must follow that thought to it's conclusion to see the difference. Sometimes it's more useful, sometimes not.
- nybble41 6y ago> Late function binding allows you to operate over heterogeneous inputs where the only thing you want to enforce is that they obey a protocol… You can do that in typeclass-based languages as well, e.g. in Haskell: {-# LANGUAGE ConstraintKinds, ExistentialQuantification #-} data Constrained c = forall a. c a => C a showAll :: [Constrained Show] -> [String] showAll xs = map (\(C x) -> show x) xs main = foldMap putStrLn $ showAll [C (1 :: Integer), C ("abc" :: String), C (3.14 :: Double)] The `showAll` function takes a list of objects that implement the `Show` typeclass. The objects themselves can be of different types. The syntax is slightly less ergonomic, but the runtime effect is much as you would expect from an OOP language—a pointer to the typeclass dictionary ("vtbl") is paired with each object, and the address of the appropriate `show` method is looked up at runtime for each element of the list.