5 ms·
FP is more like: drink(sazerac(garnish(strain(shake(absinthe(bitters(whiskey(ice(glass))))))))) Ultimately it's the same result? The difference is when you ca
by owyn 10y ago
FP is more like: drink(sazerac(garnish(strain(shake(absinthe(bitters(whiskey(ice(glass)))))))))
Ultimately it's the same result? The difference is when you can reuse and compose functions.
- threeseed 10y agoExactly. FP you start with the methods and just keep composing. With OO you start with classes and objects.
- Chyzwar 10y agoIn OOP you compose nicely as well. It is also easier to understand because it is just conversation. Me(Drinker)->drink( Sazerac(Cocktail) ->garnish() ->strain() ->shake() ->absinthe() ->bitters() ->whiskey() ->ice() ->glass() )
- sooheon 10y agoIf you want to create a bar food (Finger Food object, not Cocktail), which also could use a garnish() method, which inherits from which? Or should both objects have a father, Garnishable object to inherit from? It's clear to me that object composition is less flexible than functional composition.
- kuschku 10y agoYou just have the method as an aspect that you import into the class ;)
- Chyzwar 10y agoYou can have trait/interface. But since garnish will be doing different thing it is OK to have two implementation. FP looks nice in theoretical examples in real world not so much. In FP you will end with garnishFingerFood and garnishCocktail because you need to encode somewhere a specifics of garnish action. In OOP you will have garnish methods on Coctail and FingerFood and specifics and related knowledge how you need to perform garnish will be on object itself. OOP is really powerful concept but failing in languages that have shit implementation. Java, C++ forsake OOP principles for "performance" or are made by people that do not understand concepts (Python, PHP).
- leshow 10y agoFrom the specific example you're presenting, I don't see how you couldn't just have a Garnishable typeclass, that implements garnish differently for the FingerFood or Cocktail types. The comment that FP isn't nice in the real world is pure baloney. For lots of "real world" IO-bound types of problems there is nothing better suited than a functional programming language with powerful abstractions. Things like monads let you write code in an imperative style without losing any of the benefits of writing in the functional paradigm.
- dpkp 10y ago> In FP you will end with garnishFingerFood and garnishCocktail because you need to encode somewhere a specifics of garnish action. In OOP you will have garnish methods on Coctail and FingerFood and specifics and related knowledge how you need to perform garnish will be on object itself. You're complecting. In the real world of Clojure, you could simply define a protocol and provide different implementations of "garnish".
- yawaramin 10y agoOOP doesn't actually have the market cornered on polymorphism. As my sibling replies show, FP languages have long known how to achieve it.
- junke 10y agoGeneric functions: functional yet dynamically dispatched.
- jeremiep 10y agoGenerics aren't dynamically dispatched. Java has type erasure and C# emits variants for every instance.
- junke 10y agoNo, I was talking about (Common) Lisp's generic functions (http://clhs.lisp.se/Body/m_defgen.htm http://clhs.lisp.se/Body/m_defgen.htm). I realize I did not give enough context. Define a generic function: (defgeneric garnish (what with-what)) Specialize it on one or multiple arguments: (defmethod garnish ((c cocktail) (f fruit)) ...) (defmethod garnish ((s sandwich) (h ham)) ...) ... But you can use it like a function: (let ((currified (rcurry #'garnish :curry))) (map 'list currified items))
- jeremiep 10y agoAh my bad! :) These look very much like Clojure's protocols and multi-methods.
- junke 10y agoYes, more like multi-methods, except for standard qualifiers (:around, :before, ...) and user-defined ones. OTOH, Clojure allows you to define a custom dispatch function.
- jeremiep 10y agoAh yes, Clojure has no first-class support for aspects. Its easy enough to monkey patch definitions for the rare case when its needed. Most of the time however I try to avoid aspects as they introduce hidden behaviour in existing functions, which can be hard to reason about at scale, especially with more than a few developers.
- fnordsensei 10y agoI would probably use threading to achieve comparable readability. (def sazerac (-> (mix :ice :whiskey :bitters :absinthe) shake strain garnish)) (drink :me sazerac) I never had a Sazerac. It sounds like a nice drink.
- greydius 10y agoI would create a Cocktail data structure and an instance of Monad for it. sazerac = do add ice add ryeWhisky add bitters add absinthe shake strainInto glass add lemonGarnish main = serve $ makeCocktail sazerac
- dllthomas 10y agoHaskell really is a pretty nice imperative language sometimes.
- yomly 10y agoAnd people have the cheek to complain about parens in Lisp...
- jeremiep 10y agoIts ironic because foo() and (foo) have the same number of characters; but the later is actually data you can manipulate directly. Reminds me of the blub paradox in beating the averages[1]. [1]: http://paulgraham.com/avg.html http://paulgraham.com/avg.html
- mahyarm 10y agoI once had a comment where I translated a clojure function into it's equivalent syntax in python. It was still pretty hard to read. I think its about how lisp uses function composition for everything makes code hard to parse until you get good at it. Even with the standard practice is to hide it with macros and many small functions. https://news.ycombinator.com/item?id=11174946#11177360 https://news.ycombinator.com/item?id=11174946#11177360
- bpicolo 10y agoI like more forward-building, pipe style FP. Reading outwards on the statement level still makes things hard for me.
- dustingetz 10y agofluent interfaces from OOP are different than composition because they mutate-and-return the object reference; if the instance is aliased anywhere else in the program there is spooky-action-at-a-distance. Composition is about programming with values without mutation. As far as syntax goes it is a trivial macro to translate `x.f().g()` into `g(f(x))` (clojure actually provides it : http://clojure.org/guides/threading_macros http://clojure.org/guides/threading_macros)
- yawaramin 10y ago> they mutate-and-return.... Not necessarily; you can easily write instance methods which merely copy the existing object.
- jeremiep 10y agoThat isn't composition to me but sequencing. Function composition yields a function, not the result of applying the functions. Either you have an object with all of garnish(), strain() and whatnot on it, or each method returns the object to handle the next step in the chain. Either methods don't scale at all without modifying existing code. The real difference is that function composition gives you a reusable function you can further compose while objects keep piling up methods until you're left with god objects or indirection hell.