Going away from falsely linear time and shared mutation is one strength. You can rely on what has been assumed much stronger. Very good for concurrency.
Links for anyone interested:
https://www.infoq.com/presentations/Value-Values (1 hour version)
https://www.youtube.com/watch?v=-6BsiVyC1kM (1/2 hour version)
I think it should basically be required reading for any programmer, it's a very easy to follow look into the functional paradigm. It's also free to read online! http://learnyouahaskell.com
FP => I'll have a Sazerac
Imperative => Excuse me sir, could you take some ice, add rye whiskey, add bitters, add absinthe, shake, strain into a glass, and add a lemon garnish before bringing it to me
Ultimately it's the same result? The difference is when you can reuse and compose functions.
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() )
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 :)
(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 sazeracReminds me of the blub paradox in beating the averages[1].
Not necessarily; you can easily write instance methods which merely copy the existing object.
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.
The imperative equivalent would be:
"Give me a Sazerac!"
(imperative, hehe)
Would you mind actually making Sazerac in your FP "analogy" as well?
(-> {}
ice
(rye :2-fingers)
bitters
absinthe
shake
strain
garnish)
Now I think that strain flipped the returned type from drink to a glass with the drink.All this shows is that OO and FP are duals [1]. I don't claim to get FP perfectly, but my moment of zen was realizing this.
The value for FP comes from proper abstraction over these processes in functional terms, at which point they can be trivially implemented (few bugs, few iteration cycles to get right). This can probably be done for every problem space, question is at which the abstraction costs outweight the gain. Considering that FP becomes more and more mainstream it's probably more viable than thought in the past, still I imagine system-driven games or complex real-world simulations, with lots of side effects, would lose more from FP than they'd gain.
Some time ago I thought that, too, but I'm no longer convinced. Directly mutating values (OO style) feels more natural at first, but then you have trouble with side effects and order of execution matters more than it should, you start keeping snapshots of the whole state just to get a consistent world state during computation, otherwise this whole mess produces a whole class of bugs on its own. These problem drive you more and more into FP direction, and the FP style definitely does have its merits in this regard.
I think the following article articulates this very well:
"A Worst Case for Functional Programming?"
Second, that's not functional programming, that's calling a library function.
FP => I'll have a Sazerac
Imperative => Serve me a Sazerac
Imperative programming isn't devoid of abstractions; it just has different ones.