In Scala you opt into currying with a second parameter list; in Haskell it's the only way functions work. Once you see that add 2 3 is really (add 2) 3, partial application, sections, and point-free style all fall out for free.
In Scala, a function can take as many arguments as you list. In Haskell there's a quieter rule running underneath everything: every function takes exactly one argument and returns exactly one thing. Multi-argument functions are an illusion built out of single-argument ones.
That sounds limiting until you realize what it buys you. Because add 2 3 is secretly (add 2) 3 — apply one argument, get back a function, apply the next — half-applying a function is free, sections like (+1) are just functions, and gluing functions together with . reads like a pipeline. This is the chapter where Haskell's syntax stops looking foreign.
Look at a two-argument signature in Haskell: add :: Int -> Int -> Int. It reads like "takes two Ints, returns an Int." But the real shape is Int -> (Int -> Int) — a function that takes one Int and hands you back another function, which then takes the second Int.
You've seen this in Scala, but only when you opt in. A curried def written def add(a: Int)(b: Int): Int has type Int => Int => Int — exactly the same shape. The difference is that Scala makes the multi-parameter list def add(a: Int, b: Int) the default and currying the special case; Haskell flips it. Every function is curried, with no extra syntax. That's the whole idea — currying.
-> in a Haskell type is the same arrow as Scala's => in Int => Int. So add :: Int -> Int -> Int is Scala's val add: Int => Int => Int — a function returning a function. Haskell just hands you that for free on the ordinary-looking add a b = a + b.Here's the payoff. Since add 2 is itself a function of type Int -> Int, you can stop there and hand it around half-applied. Give it a name, pass it to map, store it — it's a perfectly good value.
The everyday superpower: build specialized functions by fixing some arguments up front. Want an "increment" function? It's just add 1. In Scala you'd reach for a curried def and write add(1), or sprinkle in placeholder underscores like add(1, _). In Haskell partial application is the default — leave off the last argument and you're done.
add :: Int -> Int -> Int add a b = a + b inc :: Int -> Int inc = add 1 -- partially applied — no second arg yet -- inc 41 ==> 42
def add(a: Int)(b: Int) = a + b then val inc = add(1) _. Scala needs the trailing _ to say "yes, I mean the half-applied function." Haskell never asks — a function with a missing argument is a function.Once you know functions are curried, type signatures read themselves. The key fact: -> is right-associative. So when you see a chain of arrows, mentally add parentheses from the right. The last type after the final arrow is the result; everything before it is consumed one argument at a time.
Walk through a real one — zipWith, which combines two lists element-by-element with a function you supply:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] -- right-associate the arrows: zipWith :: (a -> b -> c) -> ([a] -> ([b] -> [c]))
(a -> b -> c) — note its own arrows are parenthesized, so it's one argument, not three.[a].[b].[c] — the only thing left after the final arrow.Lowercase a, b, c are type variables — Scala's generics. This signature is Scala's def zipWith[A,B,C](f: (A,B) => C)(xs: List[A])(ys: List[B]): List[C], just with the noise stripped out.
a -> b -> c means "consume an a, then a b, return a c." But (a -> b -> c) in parentheses is a single thing: a function value being passed in. Parentheses around arrows turn "more arguments" into "one function argument."Take any operator, wrap it in parens, and leave a hole on one side. You get a tiny function with no lambda needed. (+1) adds one. (*2) doubles. (>3) tests "greater than 3." (10-) subtracts from ten. These are sections — partially-applied operators.
The side the hole sits on matters: (10-) is \x -> 10 - x, while (-10) would be the number negative ten, so subtraction-on-the-right is spelled subtract 10. Everything else behaves as you'd guess.
(+1) is _ + 1, (*2) is _ * 2, (>3) is _ > 3. Same idea — a hole stands in for the argument — Haskell just uses parentheses instead of an underscore.The dot operator . glues two functions output-to-input. (f . g) x means f (g x) — run g first, feed its result into f. It reads right-to-left, same as the math symbol ∘ and Scala's compose. (Scala's andThen is the flipped, left-to-right version.)
So negate . abs is "take absolute value, then negate" — apply it to -7 and you get -7 (abs gives 7, negate gives -7). Order the tiles below and watch a number flow through the pipeline.
f . g . h, the rightmost function h runs first. If you prefer left-to-right reading like Scala's andThen, Haskell has >>> from Control.Arrow — but . is the idiomatic one, so it's worth getting comfortable reading right-to-left.$ and pipelinesFunction application in Haskell is just a space: f x. It binds tighter than anything, which means nested calls pile up parentheses: f (g (h x)). The $ operator is "apply," but with the lowest precedence — so it lets you drop those parentheses.
print (negate (abs (-7))) -- parens everywhere print $ negate $ abs (-7) -- same thing, read left-to-right
Think of $ as "everything to my right is one argument." It's the closest Haskell gets to a pipe. Combined with composition, it nudges you toward point-free style — defining a function by gluing other functions together, without ever naming its argument:
-- pointful: name the argument x f x = negate (abs x) -- point-free: no x in sight f = negate . abs
That second form is exactly what Scala calls point-free too — val f = abs _ andThen negate. You're describing the shape of the computation rather than walking an argument through it. Don't overdo it: when a section or composition gets clumsier than just naming the argument, name the argument.
When a section or composition would be more awkward than just writing the function out, reach for a lambda. Haskell's \x -> x + 1 is Scala's x => x + 1 — the backslash is meant to look like a λ with its leg cut off.
Multiple arguments stack the way you'd now expect: \x y -> x + y is sugar for \x -> \y -> x + y. One argument at a time, even here.
-- Haskell Scala \x -> x + 1 -- x => x + 1 \x y -> x + y -- (x, y) => x + y map (\x -> x * x) xs -- xs.map(x => x * x)
Often the lambda has a shorter spelling: \x -> x + 1 is just (+1), and \x -> x * x can't be a section but is fine as a lambda. Use whichever reads clearest.
Everything in this chapter, in both languages. The Scala curries explicitly with =>-returning values; the Haskell gets the same shape for free. Flip the tabs.