When I learnt about there being a language concept called Currying Well I just had to go learn more about it. The Indian in me would not and could not look the other way.
Named after Haskell Curry which is not a big deal cause this man already got three programming languages named after him, Currying is essentially an operation that takes a function of two or more arguments and converts it into a pipeline of functions which take one argument each
result = f(a,b,c) = g(a)(b)(c)
// break it down
d = g(a)
e = d(b)
result = e(c)
There is also another construct called partial application which is like Currying but we pass in too few arguments, cause its sometimes more cleaner and gives you these reusable functions.
e = f(a,b)
// Now I can call `e` independently
result = e(c)
// Reuse with another argument `i`
result_i = e(i)
Well this is super ! I sure hope this works nicely in my favorite programming language Python
1
2
def f(a, b, c):
return "did i even curry {a} {b} {c}"
Umm this is embarrassing, Seems like we cannot call f(a) without passing all the 3 arguments, but I do really wanna curry tho so as the most developers do let’s find a workaround that makes everything much worse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def g(a):
def d(b):
def e(c):
return f"Welp We Curried g({a}) d({b}) e({c})"
return e
return d
result_1 = g(1)(2)(3)
# split
d = g(1)
e = d(2)
result_2 = e(3)
# same2same
result_1 == result_2
This is gonna looks so much funnier when we have a hundred curried functions and also another major issue here is that we cannot call g(1,2,3)
since g
is now a one argument function
Finna take a look at how this works on SML
1
2
3
4
5
6
fun f a b c = a ^ b ^ c;
f "do you" "know" "haskell bro?"
(* partial *)
val d = f "do you"
val e = d know"
e "haskell bro?"
Well! that was quite pleasant
This is because SML functions technically can take only one argument & all the other arguments we passed are just functions take the first conceptual argument and return another function that takes the second conceptual argument and so on, so its essentially pipelining of functions
f a b c d e
is essentially ((((f a) b) c) d) e
Guess who has crippling anxiety and wants to replicate this language construct in Python even though its absolutely unnecessary
Python being pretty cool actually has functools
which is an internal module for higher-order functions, we can make use of this to implement currying & partial applications
First up we have partial
which can be used to implement partial applications
1
2
3
4
from functools import partial
n_x = lambda a, b : a * b
ten_x = partial(n_x, 10)
ten_x(20) # 200
This is done in SML without any extra module & I also find it easier to reason about
1
2
3
fun n_x a b = a * b
val ten_x = n_x 10;
ten_x 20 (* 200 *)
We can extend this a bit to actually implement our own basic curry
function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from functools import partial
n_x = lambda a, b : a * b
def curry(f):
def g(*arg):
try:
return f(*arg) # WTF pass only one arg, need to curry bro
except TypeError: # Missing arguments
return curry(partial(f, *arg)) # OMG Recursion
return g
curried_nx = curry(n_x)
curried_nx(10, 20) # 200
ten_x = curried_nx(10)
ten_x(20) # 200
This is not fully correct cause we are not covering all the cases including args, kwargs, builtin functions, and also error handling, A proper implementation for this is available on pytoolz
The lesson here is to not follow this lesson and best leave these constructs to func languages
PS:
I made this while working through Programming Languages It is a beautiful course where I got to explore some these really cool language constructs like tail recursion, pattern matching, higher order functions etc. These were not taught to me in college cause functional languages were not a part of my curriculum, I dunno who made that call but well the internet always provides.
I made notes on the course material & programming exercises, iss up on this github repo