You Can Explain Functional Programming Using Emojis
A visual representation of Lambda calculus, Church encoding, and Y combinator
Last month, I published a free online course called “Y Combinator for Non-programmers”. In this 17-page course, I explain functional programming concepts such as lambda calculus, Church encoding, and Y combinator in a way such that people who have zero programming knowledge can understand.
I didn’t use any code to explain these concepts. Instead, I created something called “emoji puzzles” that can visualize functional code. In this article, I’ll explain how my emoji puzzles can represent and execute functional code visually.
Quick Demo
First, here’s simple, functional JS code. Try pressing Run below to see the result:
(sushi => sushi)('sandwich')
The above code can be expressed visually using my emoji puzzle below. Try pressing Run :
I’ll explain how it works shortly. If you like to teach programming, or if you like functional programming, I think you’ll enjoy this article.
Overview
This article has 10 steps:
- In the first half (steps 1 - 5): I’ll show you how simple JavaScript code can be represented visually using my emoji puzzles. You’ll be able to understand it even if you’re not familiar with JS.
- In the second half (steps 6 - 10): I’ll talk about how I used my emoji puzzles to explain functional programming concepts such as lambda calculus, Church encoding, and Y combinator.
Identity function in JS
Take a look at the following code. It’s an identity function in JavaScript that returns the argument.
// Identity function in JSsushi => sushi
If you apply the above function on a string 'sandwich'
, the result will be 'sandwich'
.
// The result will be 'sandwich'(sushi => sushi)('sandwich')
One day, I realized that the above JS code can be described visually using emojis like below. I called this an “emoji puzzle”.
(sushi => sushi)('sandwich')
The above emoji puzzle is equivalent to the earlier JS code. First, the identity function sushi => sushi
…
(sushi => sushi)('sandwich')
…is represented by the bottom two items, which are both sushi .
sushi => sushi
is representedby the bottom two items
Second, the argument 'sandwich'
to the identity function…
(sushi => sushi)('sandwich')
…is represented by the top item, which is a sandwich .
'sandwich'
is representedby the top item
That’s how my emoji puzzles can visually describe a simple JS expression. Next, let’s talk about how we can run it.
Note: To keep things simple, emoji puzzles don’t distinguish between variable names (e.g. sushi
) and strings (e.g. 'sushi'
). Therefore, both sushi
and 'sushi'
will be represented as .
Why emojis? I used emojis because they are not scary-looking for non-programers. I used food emojis because…I like food.
Running the function
I’ve added the Run button to the JS code snippet below. If you press it, you’ll see that the result is 'sandwich'
.
(sushi => sushi)('sandwich')
We can also “run” the equivalent emoji puzzle and get the same result. Try pressing the Run button below.
(sushi => sushi)('sandwich')
The result is a sandwich , which is the same as what happens when you run the equivalent JS code.
So, you can run an emoji puzzle just as you can run a piece of JS code. This is how I taught functional programming to non-programmers in my course (Y Combinator for Non-programmers)—without showing any code.
Let’s take a look at another example. Here’s a piece of JS code that’s slightly different from before. It’s a function that ignores the input and always returns 'pizza'
.
// A function that ignores the input// and always returns 'pizza'sushi => 'pizza'
Let’s run the above code with 'sandwich'
as the argument. Press Run :
(sushi => 'pizza')('sandwich')
As expected, the result is 'pizza'
. Now, this code can be represented using an emoji puzzle as follows. Press Run :
(sushi => 'pizza')('sandwich')
Just like the JS code, the emoji puzzle ended up with a pizza after running it.
What we have learned so far: Simple JS code like below can be represented using emoji puzzles.
(sushi => sushi)('sandwich')
'sandwich'
(sushi => 'pizza')('sandwich')
'pizza'
Visualizing evaluation rules
If you know how to code, you have a mental model of how function evaluation works:
- If you see
(sushi => sushi)('sandwich')
, you can quickly figure out that the result would be'sandwich'
. - If you see
(sushi => 'pizza')('sandwich')
, you know that the result would be'pizza'
. - You know what free variables and bound variables mean.
On the other hand, most non-programmers don’t have a mental model of how function evaluation works. To help them develop the mental model, I created a step-by-step visualization of function evaluation rules using emoji puzzles.
Let’s reuse the earlier example again:
(sushi => sushi)('sandwich')
On the puzzle below, try pressing the Run button. This button is a bit different from the last time—it shows every step that happens in between.
Here are the four steps it displayed:
Step 1. Label: t l r
First, we label each emoji. The top item is labeled as t (for “Top”), the left item is labeled as l (for “Left”), and the right item is labeled as r (for “Right”).
Step 2. Match: l r
Second, we check to see if some of l’s and r’s match. If they match, add the sign. In this case, both the bottom-left and the bottom-right are sushi , so there’s a match.
Step 3. Copy: t r
Third, we copy t’s to where the matched r’s are. In this case, we copy the sandwich on the top to the bottom-right.
Step 4. Remove: t l
Finally, we remove t’s and l’s. We’re left with just the sandwich at the end.
The above steps are a visual representation of how functions are evaluated. They are equivalent to how JavaScript evaluates (sushi => sushi)('sandwich')
.
By learning these rules, non-programmers will be able to evaluate functions intuitively.
Note: The above steps would look very confusing to most programmers. As programmers, we already know how to evaluate functional expressions, and we instinctively try to map the above diagrams to code in our head.
But in my testing, it seems to be less confusing to non-programmers who don’t already have a mental model of function evaluation. Furthermore, in the course itself, I slow down and spend a lot more time covering the above rules—the explanations are spread out in a full page (here). And I don’t show any code in my course—I only show emoji puzzles, so one less source of confusion compared to this article.
One thing I learned is that the feedback on my course from programmers and non-programmers are complete opposites—generally negative from programmers but generally positive from non-programmers. I believe that’s because many programmers find that using code is simpler than using the above diagrams, but many non-programmers think the opposite.
Some people have incredible difficulty understanding code or math symbols, and sometimes visual alternatives help them. I believe the more alternative teaching methods are offered in CS education, the better.
If there’s no match
Let’s take a look at the other example from earlier:
(sushi => 'pizza')('sandwich')
Try pressing the Run button and see what happens:
Here are the three steps it displayed:
Step 1. Label: t l r—this is the same as before.
Step 2. This time, there’s no match between l and r. The bottom-left is sushi , but the bottom-right is pizza .
If there’s no match, we skip step 3 (Copy: t r) and go directly to step 4.
Step 4. Remove: t l
So we’re left with a pizza . That’s how the emoji puzzle visualizes the evaluation of a function when there’s no matching variable in the function body.
More examples (optional read): Here are more examples that might be helpful for your understanding. You can press Run on each example to see the evaluation visualization. (Or press to step through manually.)
(pizza => pizza)(spaghetti => 'bread')
(spaghetti => 'bread')
(salad => 'hotDog')(curry => 'tacos')
'hotDog'
More complicated expressions
Let’s take a look at more complicated functional JS expressions and see if they can be represented using an emoji puzzle.
Check out the following JS expression, and try to guess what the result would be before pressing the Run button.
before pressing the Run button.
(sushi => sandwich => sushi)('hamburger')('chicken')
The result was 'hamburger'
. It’s because:
sushi
is bound to'hamburger'
,sandwich
is bound to'chicken'
,- And it returns the value of
sushi
, which is'hamburger'
.
Now, here’s the equivalent emoji puzzle:
Let’s break it down. First, the function expression…
(sushi => sandwich => sushi)('hamburger')('chicken')
…is represented by the bottom row:
sushi => sandwich => sushi
And the two arguments…
(sushi => sandwich => sushi)('hamburger')('chicken')
…are represented by the top and the middle rows:
('hamburger')('chicken')
How do we evaluate this function? Well, in JS, we first evaluate the function call with the argument 'hamburger'
.
(sushi => sandwich => sushi)('hamburger')('chicken')
Equivalently, in an emoji puzzle, we evaluate the bottom two rows first. We ignore the top row initially, which is shaded in blue.
Ignore the top row for now
Also, if you look at the left edge of the puzzle above, the bottom two rows correspond to the pair of 1’s. That’s how you know that you must start with the bottom two rows. In an emoji puzzle with 3 rows, you start with the pair of 1’s.
Next, let’s label each item on the bottom two rows. This time, in addition to t l r, we now have a new label m for the sandwich .
for the sandwich
Here’s the rule: The middle item on the bottom row is labeled as m (for “Middle”), and you can ignore it. You can pretend that m’s don’t exist.
Let’s take a look at the next few steps in this iteration, which is just like what we saw earlier:
This is exactly like how the earlier JS code is evaluated.
(sushi => sandwich => sushi)('hamburger')('chicken')
equivalent to the above emoji puzzle
(but it’s not done yet!)
(sandwich => 'hamburger')('chicken')
But we’re not done yet! Let’s continue to the end by pressing the Run button below:
We ended up with a hamburger , which is exactly the same as what the JS code evaluated to.
What we have learned so far: Complex functional JS expressions can be represented and evaluated using an emoji puzzle.
(sushi => sandwich => sushi)('hamburger')('chicken')
'hamburger'
(Start with the pair of 1’s)
More examples (optional read): Here’s another example that might be helpful for your understanding. Check out the following JS expression, and try to guess what the result would be before pressing the Run button.
before pressing the Run button.
(friedPotatoes => pizza => pizza)(spaghetti => spaghetti)('salad')
The result was 'salad'
. Now, here’s the equivalent emoji puzzle below. Try pressing Run . Again, remember that:
- We start with the pair of 1’s.
- The middle item on the bottom row is labeled as m, and you can ignore it.
Next, here’s slightly different JS code. Compared to the last time, there’s an extra pair of parentheses around (spaghetti => spaghetti)('salad')
, which changes the result.
before pressing the Run button.
// There’s an extra pair of parens around// (spaghetti => spaghetti)("salad")(friedPotatoes => pizza => pizza)((spaghetti => spaghetti)("salad"))
Let’s take a look at the equivalent emoji puzzle. This time, the pair of 1’s is on the top two rows instead. So we start with the top two rows this time. Try pressing Run and see what happens.
on the top two rows instead
Summary:
- In JavaScript, you can change the evaluation order of an expression by placing parentheses on different locations.
- In emoji puzzles, you can change the evaluation order by placing the numbers (e.g. 1’s and 2’s) on different locations.
Church numerals
You’re halfway there: 5 steps down, 5 more to go!
If you’re thinking of taking a break, I’d appreciate it if you could Share this article on Twitter before you close this page.
From now on, we’ll apply what we’ve learned so far to solve more interesting problems.
Here’s a function called convert
that takes a function f
as an argument. It then calls f
with (n => n + 1)(0)
.
function convert(f) {return f(n => n + 1)(0)}
Now, suppose that we apply convert
on this function: sushi => sandwich => sandwich
. What would the result be? Try to guess before pressing the Run button.
before pressing the Run button.
function convert(f) {return f(n => n + 1)(0)}convert(sushi => sandwich =>sandwich)
The result is 0
because:
sushi
is bound ton => n + 1
sandwich
is bound to0
- And it returns
sandwich
, which is0
.
Next: What if the input to convert
changes as follows? Try pressing Run on each example.
convert(sushi => sandwich =>sushi(sandwich))
convert(sushi => sandwich =>sushi(sushi(sandwich)))
convert(sushi => sandwich =>sushi(sushi(sushi(sandwich))))
The results are 1
, 2
, and 3
respectively because:
sushi
is bound ton => n + 1
sandwich
is bound to0
- So it applies
n => n + 1
to0
for the number of timessushi
is used in the function body.
What we have learned so far: If we have a function that has one of the following patterns:
// a & b can be any variable namea => b => ba => b => a(b)a => b => a(a(b))a => b => a(a(a(b)))// ...and so on...
Then, when the function is passed to convert()
, it returns the number of times a
is applied to b
in the function body.
convert(a => b => b) // 0convert(a => b => a(b)) // 1convert(a => b => a(a(b))) // 2convert(a => b => a(a(a(b)))) // 3// ...and so on...
Important: These functions that can be converted to a number using convert()
have a special name. They are called Church numerals. Each function represents a Church numeral, like this:
// Church numeral 0a => b => b// Church numeral 1a => b => a(b)// Church numeral 2a => b => a(a(b)))// Church numeral 3a => b => a(a(a(b))))// ...and so on...
You might be wondering: “What’s the point of this?” Don’t worry—I’ll tell you why Church numerals are interesting shortly. But before I do, let me explain how emoji puzzles can express Church numerals.
Here’s an emoji puzzle that represents sushi => sandwich => sandwich
, which is Church numeral 0.
sushi => sandwich => sandwich
(Church numeral 0)
Here’s what’s new: We now have the “Convert to a Number” button below the puzzle which converts it to the corresponding Church number. Try pressing it:
Other Church numeral functions can also be represented using emoji puzzles, and they can be converted to a number. And we can use emojis other than and —as long as they follow the same pattern.
hamburger => chicken => hamburger(chicken)
(Church numeral 1)
Now that we’ve covered the basics of Church numerals, we’ll talk next about why Church numerals are interesting.
Arithmetic with functions
Church numerals are interesting because they let you do arithmetic with functions. Take a look at this function:
sushi => sandwich => pizza =>sandwich(sushi(sandwich)(pizza))
It looks pretty complicated, but don’t worry too much. Now, suppose that:
- We apply the above function to the Church numeral zero (has the pattern
a => b => b
), and - Run
convert()
(from earlier) on it. - What would the result be?
Let’s take a look. Try to guess before pressing the Run button.
// Function from the aboveconst f = sushi => sandwich => pizza =>sandwich(sushi(sandwich)(pizza))// Church numeral zeroconst zero = chicken => salad => salad// What happens when you apply f to zero,// and convert it?convert(f(zero))
The result is 1
, which is 1 greater than the input Church numeral, which was 0
. In other words, 0
became 1
.
0
became 1
Here’s the secret: This function f
we used actually adds 1 to the input Church numeral. When you apply f
to a Church numeral, it returns a new Church numeral that’s 1 greater than before.
// If we apply this function to a// Church numeral, it returns a new// Church numeral that’s greater by 1.const f = sushi => sandwich => pizza =>sandwich(sushi(sandwich)(pizza))
You can try out more examples to verify this:
// Church numeral oneconst one = chicken => salad =>chicken(salad)convert(f(one))
// Church numeral twoconst two = chicken => salad =>chicken(chicken(salad))convert(f(two))
This is what I mean by “doing arithmetic with functions”:
- By using a Church numeral function that can be converted to
someNumber
… - …and the function
f
we saw earlier, - You can calculate
someNumber + 1
.
someNumber + 1
using just functions
Now, let’s see if we can explain this to non-programmers using emoji puzzles.
First, here’s an emoji puzzle representation of the Church numeral 0. You can confirm that it can be converted to by pressing the button below.
chicken => salad => salad
(Church numeral 0)
And here’s the function f
we saw earlier, represented as an emoji puzzle:
const f = sushi => sandwich => pizza =>sandwich(sushi(sandwich)(pizza))
Let’s apply f
to the Church numeral 0. To do that, we just combine the above two emoji puzzles and run it. Press Run :
The result is equivalent to sandwich => pizza => sandwich(pizza)
, which is Church numeral 1.
sandwich => pizza => sandwich(pizza)
(Church numeral 1)
What just happened: An emoji puzzle that can be converted to became a puzzle that can be converted to .
In other words: We can use an emoji puzzle to calculate someNumber + 1
.
More examples (optional read): Let’s see if we can calculate 1 + 1 = 2
using the same method. Here’s an emoji puzzle that can be converted to :
chicken => salad => chicken(salad)
(Church numeral 1)
And let’s combine it with the earlier emoji puzzle and run it. Press Run :
The result is equivalent to Church numeral 2 and can be converted to .
So, it successfully calculated 1 + 1 = 2
. Again, this is what just happened:
Multiplications
You can do pretty much any computation with Church numerals. Consider multiplication. Here’s a JS function that multiplies two Church numerals:
// Multiplies two Church numeralsconst mul = sushi => sandwich => pizza =>sushi(sandwich(pizza))
Let’s compute 2 x 3
using the above mul
function. We use two Church numeral functions—one for 2
and the other for 3
, and feed them into mul
. Take a look at the code below and press Run :
// Church numeral twoconst two = chicken => salad =>chicken(chicken(salad))// Church numeral threeconst three = curry => hamburger =>curry(curry(curry(hamburger)))const result = mul(two)(three)convert(result)
The result was 6
, so it successfully calculated 2 x 3
.
Now, let’s see if we can do the same using emoji puzzles. First, here’s an emoji puzzle that’s equivalent to the mul
function.
// Multiplies two Church numeralsconst mul = sushi => sandwich => pizza =>sushi(sandwich(pizza))
Let’s combine it with emoji puzzles that can be converted to and :
Here’s the combined puzzle. Press Run and see what happens. (Because it takes time, we’ll fast-forward it at 4x speed.)
The result is equivalent to Church numeral 6 and can be converted to .
So emoji puzzles can calculate multiplications too. In my course, I also show how we can do subtractions using emoji puzzles. Divisions are very complicated but possible.
Conditionals
In addition to arithmetic, we can also implement conditionals such as if/else
statements using emoji puzzles.
Consider the following JS code. This is a simple if/else
statement that does different things based on what x
is.
if (x === 0) {// is Zero} else {// is NOT Zero}
It turns out that if/else
statements like the above can also be expressed using Church numerals. To save time, I won’t show JS code this time and will only show the emoji puzzle. Check out the following:
if (x === 0) { ... } else { ... }
The above emoji puzzle will become:
- if you put an emoji puzzle that can be converted to on the bottom
- otherwise
Let’s try it out. First, we’ll put an emoji puzzle that can be converted to on the bottom. Press Run :
can be converted to
What just happened: We started out with , and it ended up with .
Now, what if we started out with an emoji puzzle that can be converted to ? Press Run :
can be converted to
What just happened: We started out with , and it ended up with .
As you just saw, in addition to math expressions, we can also represent conditional statements using emoji puzzles.
Column: Lambda calculus and Church encoding:
There’s a programming language called Lambda calculus (Wikipedia), created by a mathematician Alonzo Church in 1930s. It only has two features: variables and anonymous functions. Here’s a piece of lambda calculus code:
λ
is the greek alphabet “lambda”λsushi.sushi sandwich
The above code is equivalent to the following JS code. There are no strings in lambda calculus—everything is either a variable or an anonymous function.
(sushi => sushi)(sandwich)
You might have realized that all the functional JS code we represented using emoji puzzles can be expressed in lambda calculus. For example, the emoji puzzle that multiplies two Church numerals…
two Church numerals
…is equivalent to the following lambda calculus code:
λsushi.λsandwich.λpizzasushi (sandwich pizza)
So, here’s the secret: My emoji puzzles are actually a visual representation of lambda calculus. And by using emoji puzzles, lambda calculus can be explained visually to non-programmers!
a visual representation of lambda calculus
Finally: We saw that emoji puzzles, or lambda calculus, can express not only numbers and arithmetic but also conditionals. In fact, lambda calculus can express pretty much anything any programming language can do—it’s Turing complete. And this method of encoding data and operators using lambda calculus is called Church encoding (Wikipedia).
Control flow and Y combinator
Question: If we can express conditionals (e.g. if
) using functions/emoji puzzles, can we express control flow (e.g. loops) as well?
The answer is yes. We can express control flow using Y combinator.
Y combinator is a function that allows you to create a recursive function without using named functions.
Y combinator is complex, so if we go into detail we’ll need another article. In fact, I used two full pages (here and here) in my course to explain Y combinator using emoji puzzles. So here I’ll briefly explain what Y combinator is.
Take a look at this JS code. It calculates the factorial of a number using recursion.
n * n-1 * ... * 1
function fact(n) {if (n === 0) {return 1}else {return n * fact(n - 1)}}
If you run it on 5
, it calculates 5 * 4 * 3 * 2 * 1
. Press Run :
5 * 4 * 3 * 2 * 1
fact(5)
Note that the above recursive function was a named function. It had the name fact
, which was called from the function body to do recursion.
fact
function fact(n) {if (n === 0) {return 1}else {return n * fact(n - 1)}}
You’d usually use a named function to do recursion. However, if you use Y combinator, you can do recursion without using a named function.
Let me show you how. Here’s the Y combinator function yc
:
const yCombinator = sushi =>(pizza =>sushi(sandwich =>pizza(pizza)(sandwich)))(pizza =>sushi(sandwich =>pizza(pizza)(sandwich)))
Now, we’ll apply yCombinator
on another anonymous function. This time, fact
is NOT a function name, but it’s a parameter name. We haven’t used any named function yet—we’ve only used anonymous functions.
fact
is now a parameteryCombinator(fact => n => {if (n === 0) {return 1} else {return n * fact(n - 1)}})
Finally, let’s run the above function on 5
and see what happens:
5
yCombinator(fact => n => {if (n === 0) {return 1} else {return n * fact(n - 1)}})(5) // ← run it on 5
The result was 120
, so it successfully calculated the factorial 5 * 4 * 3 * 2 * 1
.
So: By using the yCombinator
function, you can create a recursive function without using named functions. It allows you to implement control flow (loops) using anonymous functions.
Of course, Y combinator can be represented using an emoji puzzle:
const yCombinator = sushi =>(pizza =>sushi(sandwich =>pizza(pizza)(sandwich)))(pizza =>sushi(sandwich =>pizza(pizza)(sandwich)))
In the final lesson of my course, I show how to use the Y combinator emoji puzzle to calculate factorials (I won’t show it here because it’s pretty complex).
In any case, you can teach Y combinator to non-programmers using emoji puzzles.
The Y Combinator I showed above is for applicative order languages like JavaScript. For normal order languages like Haskell, Y Combinator would look visually simpler, like this:
normal order languages
The emoji puzzles on this page run in the applicative order because I tried to compare it with JavaScript. However, in my course, the emoji puzzles actually run in the normal order instead, simply because Y Combinator would be visually less complex.
The bottom line is that my emoji puzzles support both applicative and normal orders of evaluation, which was nontrivial to implement.
If you are interested in learning about Y Combinator, I recommend this video: “Y Not- Adventures in Functional Programming”.
This is a talk by Jim Weirich, a legendary Ruby programmer who passed away in 2014. He gave this talk at RubyConf 2012, and I was in the audience. It was an amazing talk.
Conclusion
One-line summary: By using emoji puzzles, you explain functional programming concepts such as lambda calculus, Church encoding, and Y combinator to non-programmers—without using any code.
Why did I make emoji puzzles? As someone who is passionate about teaching, I wanted to challenge myself to explain a difficult computer science concept (like Y combinator) to non-programmers while satisfying the following constraints:
- Don’t use any code
- Don’t sacrifice rigor
- Must be doable on a smartphone in under 2-3 hours
That’s how I came up with emoji puzzles (they’re smartphone-friendly too). In the future, I plan to develop something similar with other CS topics. In the meantime, you can take a look at my course, Y Combinator for Non-programmers, here:
Thank you for reading!
- I’d love it if you could share this on Twitter. Click here to Tweet this article.
- Also, the source code is available on GitHub:
This article was published on .
About me: I’m Shu Uesugi, a full-stack developer based in California, USA.