FP you start with the methods and just keep composing. With OO you start with classes and objects.
Me(Drinker)->drink( Sazerac(Cocktail) ->garnish() ->strain() ->shake() ->absinthe() ->bitters() ->whiskey() ->ice() ->glass() )
(def sazerac
(-> (mix :ice :whiskey :bitters :absinthe)
shake
strain
garnish))
(drink :me sazerac)
I never had a Sazerac. It sounds like a nice drink. sazerac = do
add ice
add ryeWhisky
add bitters
add absinthe
shake
strainInto glass
add lemonGarnish
main = serve $ makeCocktail sazeracEither 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.
Reminds me of the blub paradox in beating the averages[1].
Not necessarily; you can easily write instance methods which merely copy the existing object.
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).
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.
You're complecting. In the real world of Clojure, you could simply define a protocol and provide different implementations of "garnish".
(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))These look very much like Clojure's protocols and multi-methods.
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.
Aspects I feel are more useful when you want to modify the behaviour of existing code you don't own.
Maybe if I had easy access to them I'd find more use cases :)
Ultimately it's the same result? The difference is when you can reuse and compose functions.