λ Scala → Haskell · ch.2 · functions & currying
➡️ Chapter 2 · One argument at a time

Every function takes one argument.
Currying, for free.

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.

1One argument, always

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.

Interactive · apply one argument at a time Watch the type shrink as each box collapses
add :: Int → Int → Int
💡
The -> 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.

2Partial application

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
🔁
Scala mirror: 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.

3Reading a type signature

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]))
  • 1st argument: a function (a -> b -> c) — note its own arrows are parenthesized, so it's one argument, not three.
  • 2nd argument: a list [a].
  • 3rd argument: a list [b].
  • Result: a list [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.

⚠️
Don't confuse the two arrows. A bare 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."

4Sections

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.

Interactive · build a section Pick an operator and operand · see the function and its result
🔁
Scala mirror: a section is Scala's placeholder syntax. (+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.

5Composition

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.

Interactive · drag to reorder the pipeline Value enters left, flows right · the . chain runs right-to-left
⚠️
Direction gotcha: in the chain 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.

6$ and pipelines

Function 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.

7Lambdas

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.

8Side by side

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.

9Check yourself

3 questions · instant feedback 0 / 3