sum = foldr (+) 0
foldr (f . g) e
rather than sum' xs = foldr (+) 0 xs
foldr f' e
where f' a b = f (g a) b
The idea is that you define loads of tiny functions at higher order, like the sum example, and these are very simple and easy to maintain because every function is a one-liner. I don't know how to respond to the complaint that the example is just syntactic, because it's fundamentally a syntactic construct (at least unless we get to the point of e.g. summing a list of functions to get their composition): the pointfree style is completely equivalent to the pointed style, it's just argued to be a less noisy syntax, that therefore increases readability and maintainability.[0] https://en.wikipedia.org/wiki/Forth_(programming_language)
[1] https://web.archive.org/web/20111007025556/http://www.latrob...
They are also a joy to design/implement* and doing it really changes how you think about programming.
* http://hashmal.github.io/shirka/ my personnal attempt at designing such a language. warning: very old, dirty, buggy code.
If you're familiar with the Maybe type (called Option in Rust and Java, and perhaps Swift?), this example might help. For concreteness, this is how it's defined in Haskell:
data Maybe a = Nothing | Just a
Consider this function, called `maybe` in Haskell: maybe :: t -> (a -> t) -> Maybe a -> t
maybe defaultVal _ Nothing = defaultVal
maybe _ f (Just v) = f v
This unwraps a Maybe value, transforming the value inside if it exists and taking a default to return if it doesn't. So, for example > maybe 4.0 sqrt (Just 9.0)
3.0
> maybe 4.0 sqrt Nothing
4.0
Now consider this function: withMinDefault :: Maybe Int -> Int
withMinDefault = maybe intMin (* 2)
Here (* 2) is a partially-applied function that doubles a number: ((* 2) 3) = 6. The advantage of defining `withMinDefault` like this instead of the equivalent "pointful" version withMinDefault' :: Maybe Int -> Int
withMinDefault' n = maybe intMin (* 2) n
is minimal at best, but the idea is that we're defining this function in terms of `maybe` directly: "withMinDefault multiplies an optional number by 2,
defaulting to INT_MIN if it's not present."
You can combine this with function composition to start working by almost exclusively manipulating functions, and (when done tastefully, as I stated at the beginning) it can greatly improve the clarity of the code for an experienced programmer who can read streams of functions with facility.Tangent but this is one reason C++ works nicely with a more functional style with lambdas - the compilers are pretty good about optimizing away any overhead resulting from function composition etc.
Haskell's competitive with everything but hand-tuned C code, consistently.
In a world where Python and Javascript are credible languages to orchestrate numeric computing, it seems ludicrous to imply that Haskell, which has superior code generation to these options in nearly every particular, is somehow "too slow."
The reason is simple: the compiler inlines the composition operator in the vast majority of (probably all?) cases, since the Haskell standard library source code marks it with an INLINE pragma:
-- | Function composition.
{-# INLINE (.) #-}
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
http://hackage.haskell.org/package/base-4.10.0.0/docs/src/GH....Sometimes you just want to reuse an existing function with 2 arguments. You name it either because you want to give it an explicit type (in many cases the exact same code can do different things depending on the type it expects). People's first exposure to point free style is often "flip", a higher-order function that takes a diadic function and swaps the order of arguments.
As an example, the "cons" function from data.text takes a character and a text object and prepends the character into the text object. It has type: Char -> Text -> Text
But what if we'd like to do this repeatedly? Ideally we'd reuse a function like foldl (reduce in javascript parlance). What we'd like is to have a function like conss that has type Text -> [Char] -> Text. Sadly, foldl expects its reducer function to take something more like Text -> Char -> Text, which is really close to what we already have with cons.
A point free solution feels pretty natural here:
conss :: Text -> [Char] -> -> Text
conss = foldl (flip cons)
We might say, "I actually want to prepend the same prefix to hundreds of strings." A simple point free way to think about reusing our old code: prepend :: [Char] -> Text -> Text
prepend = flip conss
Often times we use point-free style when we're building other structures. Some time ago I wrote a text adventure game engine to explain Haskell to some kids, and it has this line (https://github.com/KirinDave/Rag/blob/master/src/Rag/Parser....): buildMaze :: [(Int, Room)] -> MazeDefinition
buildMaze = foldr (uncurry Map.insert) Map.empty
Again, a point free version of this is much more readable and direct than if I were to pattern match into the arguments. That would require a binding both on the outside for the array and in the inner reducer function, adding tons of noise: buildMaze :: [(Int, Room)] -> MazeDefinition
buildMaze definitions = foldr reducer Map.empty definitions
where reducer = \(m, (i,r)) -> Map.insert i r m
Sometimes, point-free style can also help with very abstract code. A common way to define the functor instance for the Free monad, for example, is reasonable to follow in point free notation but pretty brutal without it: instance Functor f => Monad (Free f) where
return = pure
Pure a >>= f = f a
Free x >>= f = Free ((>>= f) <$> x)
Trust me, you don't want to see the lambda-ized version of that inner bind. It's really, really bad. It also loses the clarity this one offers about the fact that at the end of the day, you make a big chain of functions and then fmap it (which is actually pointing to a massive performance flaw with this implementation free monad!).I agree that the author didn't really make their point here. What's more, using the language they did will always make point free notation look awkward. Point free notation thrives in environments where partial application is automatic.
>Sometimes, especially in abstract situations involving higher-order functions, providing names for tangential arguments can cloud the mathematical concepts underlying what you’re doing.
Sometimes? When?
>point-free notation serves as an example of how subtle changes to the way you think about or define your code can have big impacts on readability.
In the end, we're defining `atLeastTwo` and using it identically, so it would be much more compelling if there was an example in which things were, in fact, more readable. This example is just syntactic. It's like arguing that `const atLeastTwo = (x) => x >= 2` is better than `function atLeastTwo(x) { ... }`
> It’s your job as a developer to be cognizant of these tradeoffs.
It's just that this piece isn't really helping me get there.
I think the author is suggesting that we define `atLeastTwo` in terms of an `atLeast` primitive, i.e., currying and partial application. It's clear that this is going to make composition easier, but it is relying on it. Now, I'd like to see how point-free, as a paradigm, yields better results in terms of readability or error prevention.