Currying explained for Python programmers

One thing that strikes Haskell beginners with its weirdness is the notion of a curried function. Since all functions are curried by default in Haskell, it’s safe to say that it’s essential to understand what currying is to be able to delve deeper into Haskell.

Although the idea of currying is not particularly hard to grasp, it sounds a bit weird since it’s kind of a crazy way of approaching functions—especially due to the tuple-based ontology familiar from calculus classes.

Before even trying to define what currying is at an abstract level, let’s go ahead and define a “function”1 f in Python to fiddle with its behavior.

>>> def f(x, y, z): return x**3 + y**2 + z
...
>>> f(2, 2, 2)
14

f is simply a function that returns x3+y2+z and it works as intended. Now, what concerns us is what happens if we do f(2). Of course as expected, Python passively-aggressively tells us that we made the mistake of not providing f with enough arguments.

Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: f() takes exactly 3 arguments (1 given)

This is the expected behavior with the common conception of a function; a function must be fed the number of arguments it requires. In Haskell though, all functions show curried behavior: they are defined as the sequence of multiple single-argument functions. So if I were to define the same function f in Haskell, the application of f to a single argument would return another function expecting to operate on the remaining two arguments. If I were to apply that function to another single argument, that would return yet another function expecting to operate on the third one of the three arguments f accepts in total. You get the idea.

To make things a bit more concrete, let’s make ourselves a curried function in Python.

Let’s play with our curried f; if we call it with a single argument, we get a new function that we will store to g.

>>> curried_f(2, 3, 4)
21
>>> g = curried_f(2)
>>> g(3, 4)
21

Or, we can do this

>>> g = curried_f(2)
>>> h = g(3)
>>> h(4)
21

But why would anyone want this? The answer may not be apparent in Pythonian thinking. But consider map, for example. We can map over a list if we have a function expecting a single argument. If we were to use a function that takes multiple arguments, we would have to use lambda or define a new function. But with this curried function we made we could compute 43+32+z for z=1,2,3,4 simply like this

>>> map(curried_f(4, 3), [1, 2, 3, 4])
[74, 75, 76, 77]

Or we could plug it into reduce like:

>>> reduce(curried_f(2), [1, 2, 3, 4])
17436

Of course, these high-level operations such as map and reduce are not used that frequently in Python, but they go so well with Haskell’s take on programming that being able to get things done with these higher level functions is kind of like the Haskell programmer’s idiomatic competence. If these stuff sound interesting to you, definitely take a look at what Miran Lipovača has to say about them.

  1. I put quotation marks there because the term function in Python does not extend beyond being a useful analogy. Python functions are not functions in the same sense that Haskell functions are functions; the latter are actual mappings between sets.

::...
免责声明:
当前网页内容, 由 大妈 ZoomQuiet 使用工具: ScrapBook :: Firefox Extension 人工从互联网中收集并分享;
内容版权归原作者所有;
本人对内容的有效性/合法性不承担任何强制性责任.
若有不妥, 欢迎评注提醒:

大妈的多重宇宙 - YouTube

全新自媒体:科幻/读书/说故事...欢迎订阅;

或是邮件反馈可也:
askdama[AT]googlegroups.com


订阅 substack 体验古早写作:
Zoom.Quiet’s Chaos42 | Substack


点击注册~> 获得 100$ 体验券: DigitalOcean Referral Badge

关注公众号, 持续获得相关各种嗯哼:
zoomquiet



粤ICP备18025058号-1
公安备案号: 44049002000656 ...::