I realized I started using lambda without explaining what that is. My
apologies.
In math class you probably defined functions by writing something like the following:
f(x) = x * 2
This defines a function named f which takes a value x and returns twice that
value, 2 * x.
In Python we would write this as:
def f(x):
return x * 2
In math and in programming, we can have functions that take functions as arguments. For example:
g(h, x) = h(h(x))
The function g has two parameters: another function h and a value x. It
applies h to x twice. Let's look at what happens when we apply g to f,
from above, and 10:
g(f, 10) = f(f(10))
= f(10 * 2)
= f(20)
= 20 * 2
= 40
We can write the same thing in Python:
def g(h, x):
return h(h(x))
g(f, 10)
OK, so what's this got to do with lambda? Well, mathematicians and programmers
often want to define a new function without giving it a name, like f. These
functions are usually used only once as an argument to another function. In math
a function without uses the greek symbol lambda and in Python we use the word
lambda.
In math we write λ VARIABLE. BODY:
λ x. x * 2
In Python we write lambda VARIABLE: BODY:
lambda x: x * 2
Instead of writing this:
g(f, 10)
We could write this:
g(λ x. x * 2, 10)
In Python that looks like:
g(lambda x: x * 2, 10)
So, in our inner_product example we could have written this:
def compare_two_genotypes(Xim, Xjm):
return hl.int((Xim == 1) & (Xjm == 1))
def add(l, r):
return l + r
shared_hets = X.T.inner_product(
X,
compare_two_genotypes,
add,
hl.float(0),
hl.agg.sum
)
And maybe we should have! Using long names helps us understand the code. Here's the lambda version again:
shared_hets = X.T.inner_product(
X,
lambda Xim, Xjm: hl.int((Xim == 1) & (Xjm == 1)),
lambda l, r: l + r,
hl.float(0),
hl.agg.sum
)