Currying
Function currying is a specific kind of function transformation where we translate a single function that accepts multiple arguments into multiple functions that each accept a single argument.
This is a "normal" 3-argument function:
box_volume(3, 4, 5)
This is a "curried" series of functions that does the same thing:
box_volume(3)(4)(5)
Here's another example that includes the implementations:
def sum(a, b):
return a + b
print(sum(1, 2))
# prints 3
And the same thing curried:
def sum(a):
def inner_sum(b):
return a + b
return inner_sum
print(sum(1)(2))
# prints 3
The sum
function only takes a single input, a
. It returns a new function that takes a single input, b
. This new function when called with a value for b
will return the sum of a
and b
. We'll talk later about why this is useful.
Assignment
In Doc2Doc, depending on the type of text file we're working with, we sometimes need to transform the font size of the text when it comes time to render it on the screen.
Fix the converted_font_size
function. We are using a 3rd party code library that expects our function to be a curried series of functions that each take a single argument.
converted_font_size
should just take a single argument,font_size
and return a function that takes a single argument,doc_type
. That function should return thefont_size
multiplied by the appropriate value for the givendoc_type
.
def converted_font_size(font_size, doc_type):
if doc_type == "txt":
return font_size
if doc_type == "md":
return font_size * 2
if doc_type == "docx":
return font_size * 3
raise ValueError("invalid doc type")
Solution
def converted_font_size(font_size):
def doc(doc_type):
if doc_type == "txt":
return font_size
if doc_type == "md":
return font_size * 2
if doc_type == "docx":
return font_size * 3
raise ValueError("invalid doc type")
return doc
Your solution works because it implements function currying, which means it transforms a function that conceptually takes two arguments into a function that takes one argument and returns another function to accept the second argument. Here’s a breakdown:
Curried Function Structure:
converted_font_size(font_size)
takes a single argument (font_size
) and returns a new function.The inner function
doc(doc_type)
takes a single argument (doc_type
).
Processing the Input:
When you call
converted_font_size(12)
, for example, it returns thedoc
function withfont_size
set to 12.Then, calling
doc("md")
(i.e.,converted_font_size(12)("md")
) checks thedoc_type
:If
doc_type
is"txt"
, it returnsfont_size
(12).If
"md"
, it returnsfont_size * 2
(24).If
"docx"
, it returnsfont_size * 3
(36).
If the
doc_type
is invalid, it raises aValueError
.
Why It Meets the Assignment:
It adheres to the requirement that each function in the chain accepts a single argument.
The design lets you specify the font size first and then, depending on the document type, adjust that size with a multiplier.
This approach is useful in scenarios where you need to configure part of a process (in this case, setting a base font size) and then apply different transformations based on additional input (the document type).
Why Curry?
It's fairly obvious that:
def sum(a, b):
return a + b
is simpler than:
def sum(a):
def inner_sum(b):
return a + b
return inner_sum
So why would we ever want to do the more complicated thing? Well, currying is often used to change a function's signature to make it conform to a specific shape. For example:
def colorize(converter, doc):
# ...
converter(doc)
# ...
The colorize
function accepts a function called converter
as input, and at some point during its execution, it calls converter
with a single argument. That means that it expects converter
to accept exactly one argument. So, if I have a conversion function like this:
def markdown_to_html(doc, asterisk_style):
# ...
I can't pass markdown_to_html
to colorize
because markdown_to_html
wants two arguments. To solve this problem, I can curry markdown_to_html
into a function that takes a single argument:
def markdown_to_html(asterisk_style):
def asterisk_md_to_html(doc):
# do stuff with doc and asterisk_style...
return asterisk_md_to_html
markdown_to_html_italic = markdown_to_html('italic')
colorize(markdown_to_html_italic, doc)
Last updated