5 ms·
At some point I realized I don't really need OOP itself, but I like a couple of bits it provides. Mostly: - The syntax. I like to be able to say thing.doSometh
by captainmuon 12y ago
At some point I realized I don't really need OOP itself, but I like a couple of bits it provides. Mostly:
- The syntax. I like to be able to say thing.doSomething(). It doesn't always make sense, but sometimes subject-verb syntax is more natural than a function call.
- Polymorphic dispatch (is that the term?) to replace if blocks. Instead of `if (thing is Car) thing.drive() else if (thing is Boat) thing.swim()` its just thing.move(). Pattern matching in functional languages solves this in a different way.
- Interfaces are nice.
I guess I could be happy in a language that just has structs and functions, and some help in form of pattern matching, multimethods and so on.
- thisjepisje 12y agoRegarding your second point, what would be the functional equivalent?
- GeneralMayhem 12y agoInfix notation in Haskell is close, except that most of the time if verb syntax is convenient you're expecting side-effects.
- Ixiaus 12y agoHe means pattern matching functions, which are idiomatic in Erlang but "dirty" in Haskell (where you would want to solve it better with a type class, or something else more general). Erlang myfun({duck, "My Duck's Name"}) -> dosomething; myfun({dog, "My Dog's Name"}) -> dosomething; myfun({cat, "My Cat's Name"}) -> dosomething. Erlang also has arity matching (this function is different than the one above it): myfun({cat, "My Cat's Name"}, cage) -> dosomething. Case statements are one way of doing that but those are considered a messy style in Erlang because they end up nesting so deeply - keeping it all out in small little functions and using pattern and arity matching is the "right" way. You can do the same (except for arity matching) in Haskell with function arg pattern matching and Haskell also has some very nice projection tools for it too: Haskell newtype Name = Name { unwrapName :: String } data Animal = Cat | Dog | Duck deriving (Eq, Show, Ord) myfun :: Maybe Animal -> Name -> String myfun (Just a) (unwrapName -> name) = (show a) ++ "'s name is: " ++ name myfun Nothing _ = "No animal given!" > let n = Name "Fido" > myfun (Maybe Cat) n > "Cat's name is: Fido" > > myfun Nothing n > "No animal given!" There are many problems with that function and I could probably eliminate the multiple function clauses and reduce it to one clause by getting rid of the pattern matching and using the maybe function: myfun :: Maybe Animal -> Name -> String myfun a (unwrapName -> n) = maybe default formatAnimal a where default = "No animal given!" formatAnimal t = printf "%s's name is: %s" (show t) n That would be the more idiomatic way I would do it - pattern matching isn't "bad" in Haskell and you'll see it used quite often in utilities and libraries. It's considered good form to move stuff like that into a utility and give it a clear name (like the maybe function above that takes our formatAnimal function) then use that in your application code so it becomes obvious what's going on. Pattern matching, deeply nested ifs and cases, etc... are somewhat hard to parse for the eye.
- thisjepisje 12y agoJust getting into Erlang right now, this is very helpful.
- codygman 12y agoWhat is concept is the: newtype Name = Name { unwrapName :: String } (unwrapName -> name) I'm not familiar with this, are you pattern matching on a function type?
- dezgeg 12y agoIt's the View Patterns extension: https://ghc.haskell.org/trac/ghc/wiki/ViewPatterns https://ghc.haskell.org/trac/ghc/wiki/ViewPatterns
- l_dopa 12y agoYou would pass in a function or datatype containing a function, which is exactly what the OO code does. No language features beyond first class functions necessary.
- captainmuon 12y agoI was thinking of something like in F# (disclaimer: I've never used it and copied this from MSDN): type Shape = | Rectangle of height : float * width : float | Circle of radius : float let matchShape shape = match shape with | Rectangle(height = h) -> printfn "Rectangle with length %f" h | Circle(r) -> printfn "Circle with radius %f" r Admittedly, this is not too far from a simple `if`, but I believe the language ensures that you handle all cases. The difference to OOP is that the code is in one place, with the function, instead of distributed, with all objects. It makes it easier to add functions, but harder to add new types. Often, this is the better option, but if you are writing truely extensible code (a library that gets passed custom objects, or an application with add-ins) then you want to go the OOP way.
- jeffreyrogers 12y agoGood points. The D language has a way of providing your first point without resorting to objects. Basically, the compiler just transforms a statement like `str.length()` into `length(str)`. The other two seem harder to do in a simple way, but I'm sure someone has come up with something.
- Ixiaus 12y agoMonads, modules, and type classes (the bare essentials) make me never want to go back to Object Oriented Programming. That's not to mention all of the really clean and elegant software now blossoming in the Haskell community (Lens, Applicatives, Monoids, GADTS, pipes, etc...)
- noblethrasher 12y agoSo far, it seems that a well-designed OO language can get you closer to the best of both worlds than if you start from ML-style functional languages. Consider that C# let's you have all three of your essentials: It doesn't have higher-kinded types, but it does have monad comprehensions (LINQ + SelectMany)… the special syntax is nice but not necessary. It has type classes in the form of implicit conversions. It has modules in the form of static classes. C# even lets you do Smalltalk-style OO without too much fuss. The biggest problem with C# and pethaps OO in general is the lack of intellectual curiosity.
- dllthomas 12y ago"It has type classes in the form of implicit conversions." Could you expand on this?
- tomp 12y agoAlso: polymorphic dispatch is faster with OO (which is what Rust/Servo people have been figuring out recently).
- Dewie 12y agoWhat does 'faster with OO' mean? Is there some implementation detail that is tied to, or heavily associated with, OO?
- lostcolony 12y agoI assume it's based on (this is a bit hand wavy) the fact that in an OO language, calling the method merely needs to resolve the address space of the method. With pattern matching, you first have to perform the pattern match to figure out what instruction to execute, and then that resolves to an address. That is, because your data and function are bundled together, having the data means you also know what function to call. Decoupling them, as in a functional paradigm, and passing the data into a pattern match, you have to perform some logic upon the data to figure out what function to apply to it.
- noblethrasher 12y agoBut, a series of pattern matching expressions can compile down to a bunch of sealed/final classes that descend from a common base class. In that case, it would still end up using a vtable[1]. I think this is how it works in F# and Scala. [1] I think that the JITs for the CLR and JVM can do better than vtables in the cases of sealed/final classes.
- seanmcdirmid 12y agoI am sure it doesn't work with way in either scala or F#. You can use classes to represent structure, but the matching is still extrinsic and can't use a table given the flexibility of both languages (you don't just match on class).
- 12y ago
- Svenstaro 12y ago> I guess I could be happy in a language that just has structs and functions, and some help in form of pattern matching, multimethods and so on. So pretty much Rust you mean?
- nathan7 12y agoI think you'd be quite happy in Rust, if you substitute trait methods for multimethods too.
- stdbrouw 12y agoExactly. As in any situation, to understand what someone is saying, you have to know who they are talking to. People who hate OO always refer to overly bloated systems that treat OO as a philosophical foundation, something to be religiously adhered to. I'm sure those systems exist and I'm sure that they're horrible. But, y'know, it's just awfully nice to have an easy way to define structures and functions that operate on them, and the ability to use some structures interchangeably with other, related ones. Objects or Haskell-like records, I'm happy with either. I like multiple inheritance but mixins are pretty neat too. Encapsulation is a good thing, but I could probably learn to live with any kind of decent modularity and separation of concerns. Et cetera.
- mjw 12y agoAgreed. What I see as the core principles of OO (ad-hoc polymorphic dispatch and abstract data types) are present, useful and important in the FP world too. OO helped to bring attention to these ideas even if it didn't capture them in their purest and most general form. So I think we should be careful not the throw the baby out with the bathwater when criticising OO. > Pattern matching in functional languages solves this [polymorphic dispatch] in a different way. Pattern-matching doesn't give you late binding / ad-hoc polymorphism though, so while it's useful to replace static if statements or case statements, it's not a full replacement for OO's polymorphic dispatch. For that you need things like typeclasses in Haskell or multimethods in lisps. Wadler's famous expression problem is quite relevant here: http://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt http://homepages.inf.ed.ac.uk/wadler/papers/expression/expre...
- pacala 12y agoLogically, closed extension and open extension are different things. OO conflates them together for no reason other than having the polymorphic dispatch hammer. FP equivalents for polymorphic dispatch are pattern matching for closed extension and plain old first class functions for open extension.
- Dn_Ab 12y ago> Polymorphic dispatch (is that the term?) to replace if blocks. It looks like you're talking about Predicate Dispatch. http://c2.com/cgi-bin/wiki?PredicateDispatching http://c2.com/cgi-bin/wiki?PredicateDispatching
- slowmovintarget 12y agoPolymorphic dispatch is the specific reason for polymorphism in Object-Oriented Programming. It is more generically known as "dynamic dispatch": http://en.wikipedia.org/wiki/Dynamic_dispatch http://en.wikipedia.org/wiki/Dynamic_dispatch You'll encounter the term in Bertrand Meyer's Object-Oriented Software Construction for example: http://www.goodreads.com/book/show/946106.Object_Oriented_Software_Construction http://www.goodreads.com/book/show/946106.Object_Oriented_So...
- Dn_Ab 12y agoDynamic dispatch is a special case of predicate dispatch. In particular, captainmuon pointed out that: >Pattern matching in functional languages solves this in a different way. captainmuon wanted a more flexible way of dispatching on types similar to what can be done with Pattern matching (which is also a special case of predicate dispatch). In the post I linked to above this example is given: Basically, instead of basing the dispatch on an "is_a" check, you check whether a general predicate is valid on the argument. So, in imaginary syntax instead of writing int foo( int a, int b): if a > 0 return a else return b-a you'd write: int foo ( gt_zero? a, int b): return a int foo ( int a, int b): return b