Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome back.
In our last lesson on function composition, we saw how Haskell could elegantly create a 2D mapping function with map . map. I mentioned that this was possible because of a concept called currying, which allows functions to be applied to their arguments one at a time. Today, we'll explore that concept in detail.
This lesson focuses on currying, a fundamental technique in functional programming. We'll start with its formal definition and historical roots in logic and mathematics. Then, we'll see how it's a core design principle in languages like Haskell and the ML family. Finally, we'll clarify the relationship between currying and the closely related idea of partial application.
Your goal for this lesson is to be able to apply currying to transform multi-argument functions, referencing its origins in logic and its central role in ML and Haskell.
1. The Formal Concept and Origins of Currying
At its core, currying is the process of transforming a function that takes multiple arguments into a sequence of functions, each taking a single argument.
Instead of a function f that takes two arguments, say x and y, and computes a result f(x, y), we create a new function g. This new function g takes only the first argument x and returns another function. This returned function then takes the second argument y and produces the final result. So, the call becomes g(x)(y).

This technique isn't just a programming trick; it has deep roots in mathematical logic. To understand its formal definition and history, please read the following sections from the Wikipedia article on the topic.
This article provides an excellent overview of currying, including its mathematical foundation and historical context, which directly addresses key parts of our learning outcome.
Please read the following sections: The introductory section (before the 'Motivation' heading) to get the main idea. The 'History' section to learn about the logicians Haskell Curry, Moses Schönfinkel, and Gottlob Frege. The 'Definition' section for the formal mathematical transformation. The 'Logic' section to see the connection to the Curry-Howard correspondence.
As you read, take note of these key points:
- The Transformation: A function of type
(X × Y) → Zis transformed into a function of typeX → (Y → Z). The first takes a pair of arguments; the second takes one argument and returns a function. - The History: The concept is named after Haskell Curry, but the idea was developed earlier by Moses Schönfokenkel and can be traced back to Gottlob Frege.
- The Logical Equivalence: Under the Curry-Howard correspondence, this transformation is equivalent to the logical rule of exportation:
((A ∧ B) → C) ⇔ (A → (B → C)). This connects the structure of programs to the structure of proofs, a central idea in theoretical computer science.
2. Currying in Practice: The Haskell Approach
While you can manually curry functions in many languages (as we'll see later), some languages are designed with currying as a default, fundamental principle. Haskell and languages in the ML family (like OCaml and F#) are the most prominent examples.
In Haskell, all functions are considered to take only one argument. What looks like a multi-argument function is actually syntactic sugar for a curried function.
The HaskellWiki page on currying provides a concise and direct explanation of how currying is integrated into the language's design.
Read the first few paragraphs, down to the end of the div example. Focus on how the type signature a -> b -> c is interpreted as a -> (b -> c) and how function application f x y is interpreted as (f x) y.
This "curried by default" nature is a cornerstone of Haskell's design. To see this mechanism in action, let's watch a video that breaks it down step-by-step.
This video from the 'Functional Programming in Haskell' series provides a clear, visual explanation of how curried functions are evaluated and how their types are constructed.
Please watch the sections from 02:01 to 09:16. Pay attention to: How the plus function consumes one argument and transforms into a new function (02:01 - 03:31). The process of working backward from the result to determine the function's type signature (03:31 - 07:15). How Haskell's syntax uses implicit bracketing for types (right-associative) and function application (left-associative) to make currying feel natural (07:15 - 09:16).
The video illustrates a key mental model for functional programming: functions are not just procedures but also values that can be created and returned. When you give a curried function its first argument, you are not just providing data; you are creating a new, more specialized function.
Given your familiarity with lambda calculus from your multi-agent AI studies, you can think of this transformation as f(x,y) becoming λx.(λy.f(x,y)). Haskell simply makes this the default representation.
3. Currying vs. Partial Application
The primary benefit of currying is that it makes partial application seamless and elegant. Partial application is the act of fixing a number of arguments to a function, producing another function of smaller arity.
The two concepts are often confused, but the distinction is important:
- Currying is a transformation that converts a function taking multiple arguments into a chain of functions each taking a single argument.
- Partial Application is the application of a function to fewer than its total number of arguments.
In a curried language like Haskell, partial application is a natural consequence of the evaluation model. If you provide add (which takes two arguments) with only one argument, add 5, you don't get an error. You get back the partially applied function that adds 5 to anything.
This next video explains this relationship very clearly.
Haskell for Imperative Programmers #7 - Partial Function Application & Currying
Philipp Hagenlocher's video connects the theory of currying directly to the practical utility of partial application.
Watch the video from the beginning to 03:28. Focus on: The introduction, which frames partial application as a result of currying (00:00 - 01:00). The explanation of what happens when you call add 1 in Haskell (01:15 - 02:49). The practical example of creating a doubleList function by partially applying map (02:34 - 03:28).
The doubleList = map (*2) example is a perfect illustration of why this matters. We created a new, specialized function without writing a new def or lambda. This is the mechanism that powered the map . map composition from our previous lesson. The first map in the composition is partially applied, creating a new function that is then ready to be composed with the second map.
For a more formal distinction, let's return to the Wikipedia article.
This section of the Wikipedia article uses formal notation to make the distinction between currying and partial application unambiguous.
Please read the section 'Contrast with partial function application'. Note how it contrasts the type signatures and evaluation of a curried function versus a partially applied one.
Conclusion
In this lesson, we've unpacked the concept of currying and its role in functional programming.
Key Takeaways:
- Currying as a Transformation: Currying transforms a function that accepts multiple arguments,
f(x, y, ...z), into a chain of functions that each accept a single argument:g(x)(y)...(z). - Logical and Historical Roots: The concept originates with logicians like Frege and Schönfinkel and was popularized by Haskell Curry. It has a direct parallel in logic via the Curry-Howard correspondence.
- Central Role in Haskell/ML: In these languages, all functions are curried by default. A function type
a -> b -> cis syntactic sugar fora -> (b -> c), meaning a function that takes anaand returns a function of typeb -> c. - Enabling Partial Application: Currying makes partial application a natural and syntactically lightweight operation. Applying a function to fewer than its total arguments simply returns a new, specialized function.
Preview of the Next Lesson:
We've seen how partial application flows naturally from currying in Haskell. In our next lesson, we'll focus explicitly on partial application as a technique in its own right. We will compare the implicit approach in Haskell with the more explicit methods required in Python, using its functools.partial utility, to understand the design trade-offs involved.