I’ve been wanting to give my eldest child (Eliezer, now 10) a chance to learn to code for about a year now, with little success. My second child (Gavriella, now 8) is also ready to start learning some coding. “Ready” to me means “they have a decent enough grasp of written English.” (Yakov, my 6 year old, also wants in, but unfortunately is going to have to wait a bit.)
I know how I learned to code: sitting at a DOS prompt since the age of 2 and reading a massive “Teach Yourself QBasic in 21 Days” book. For various reasons, this doesn’t seem to apply to the next generation. I’ve looked into other modern approaches, including the graphical programming environments. My kids enjoyed some of this, but this week told me that “it’s fun, but we’re not learning anything.”
Previously, I tried teaching Eliezer some Haskell by writing up lessons for him. Whether because of my writing or his interest, it didn’t work at the time. I decided to not go down this path again, and an hour ago sat down with Eliezer and Gavriella for their first lesson. I’m winging this completely, but here’s the approach I’m taking:
This is the first of these summary blog posts. Caveats:
I considered making these private notes for myself instead, but thought some others may be interested, so I’m posting publicly.
Without further ado!
hello-world.pyfile and demonstrated you could write the same stuff from the REPL in a file.
This was the point where Eliezer commented that it looked a lot like the Haskell he’d seen. I’m not sure what it was, but somehow it clicked with him that whatever scared him off of Haskell previously wasn’t a real issue. We decided together to switch over to learning Haskell instead, which I’m quite happy about (more because I know the language better than anything else).
2 + 3 + 4was, they got that
2 * 3 + 4was, they got that too
2 + 3 * 4, and I was surprised to find out that they knew about order of operations already. Yay school system.
(2 + 3) * 4.
print 2 + 3. They had some inkling that this wouldn’t work, but couldn’t be sure of why.
Int, and defaulting to
Num a => a, blah blah blah, this is fine for first lesson)
print 2 + 3because of order of operations (just like math) really is
(print 2) + 3, what does that mean?
fart = print "Sorry, I farted"lots of giggling. What is the type of fart? Is it a number? No. Is it a dog? No. It’s something that you do, or the computer does. That’s what an action is. (Gavriella translated some words into Hebrew at that point, and the ideas clicked. Got to remember: they’re learning both programming languages and how to learn things in English at the same time.)
print 2 + 3doesn’t make sense!
print (2 + 3).
Note: Somewhere above, I briefly showed them that you could use
notation and put multiple
I started them off with:
main = do | -- prompt started here
They both got the answer, one of them with a bit of help:
main = do print "2+3+4" print (2+3+4)
Things to note:
print "Hello World"resulted in output that kept the double quotes. I’ll have to explain at some point about
putStrLn, but that can come much, much later.
I don’t want to plan out what to cover next time too intricately, because I want to experiment with them and bounce things around. I’m thinking about showing them how to create their own functions, maybe with lambda syntax, not sure.
I didn’t expect to be writing the second blog post only 12 hours after the first. But when the kids came downstairs this morning, they were unbelievably excited to do more coding. So here we are!
Eliezer and I discussed using more visual tooling (like Code World or Jupiter) for doing the learning, and I showed him what it looked like. It seems he’s got quite a bit of his father’s preferences, and wanted to stick to plain text for now. We’ll probably circle back to that kind of stuff after they get the basics. It will also give me a chance to get more comfortable with those offerings. (Thanks all for the recommendations on Reddit.)
One final note. I’m extremely happy that we went with Haskell after today’s lessons. Concepts like variable replacement which are natural in Haskell were great teaching tools. I obviously don’t have enough data to prove this yet, but I’m more strongly believing the long-held theory that Haskell is easier for brand new programmers than those familiar with imperative programming.
I moved us over to VSCode, with only syntax highlighting set up. Previously,
I’d used my emacs setup with intero, and the red squiggles let them know they’d
messed up too early. I used the docked terminal at the bottom, taught them to
save, and showed them to press “up enter” in the terminal to rerun
stack runghc foo.hs. Perhaps not the ideal setup, but definitely good enough.
main = print "hello world"
main is defined as other thing
main = print hello hello = "hello world"
They were not surprised at all that this just worked
hello = 5 + 6, both got confused about whether it would print
5 + 6, reminded them of strings
hello = five + six?
Both agreed it wouldn’t work. I typed in
five = 5, and ask if it would
work. They agree that it won’t, and I show them GHC’s error message.
“Variable not in scope.” Computers talk funny, just means “I don’t know what six is.” Instilled that they’ll eventually be able to understand those massive error messages, but not to worry about it yet.
Exercise: five the program. Both of them got it no problem.
I was about to demonstrate
five = 4, and then Gavriella figured it out for
herself! They understand that, again, “computers are dumb.” Names are just
there for our benefit, computer doesn’t care.
five + six * seven (with appropriate variable definitions), they get
that answer, and then
(five + six) * seven, they get that too.
Now I define
fivePlusSix = five + six, and change to
hello = (fivePlusSix) * seven
(direct replacement). They’re fine with this. Yay replacement.
Point out that the parens are now unneeded and remove them. Again, no problem.
Parens just tell us “do this first”, not needed for one thing
hello = 5 * 2, no problem.
hello = double 5? They figured out that it won’t work cuz computers be dumb
double? Eliezer guessed
* 2, which is really close, but we’re not ready for sections, and I want to teach standard function stuff first.
double x = x * 2, that
xis a variable, and a function argument. They don’t know algebra yet, but got this fairly quickly.
hello = addFive (double 5)
addFive, and he started spacing things like me without any prompting. Cool.
main, oh, I got an action! I need to print
hello, what’s that? Oh, it’s
addFive .... That means I gotta figure out
double 5? Oh, it’s
10. So that’s
minusTwowas never (terminology break, I taught them that it wasn’t used).
minusTwois also a function. It needs one number. Let’s try something wrong:
minusTwo addFive (double 5). That’s wrong: it means “apply
minusTwoto two values” but it only needs one number.
addFive (double 5)into one thing. Ask them what we can use. They played with idea of double quotes, and then they figured out the parens! Awesome!
minusTen. She did it.
Gavriella kept playing with the code (wow, she’s focused on this). She decided
she wanted to do division. That was more complicated, but she persevered. I
taught her about
div being a function that takes 2 arguments. She didn’t know
anything about remainders, and was confused that
div 15 2 worked at all. I
taught her about
divMod, and she did great.
Eliezer asked me a few questions about programming yesterday, and I ended up demonstrating a little bit about pattern matching. He then had a lot of fun showing a friend of his how programming works. I may end up giving some lessons to some of my kids’ friends in the next few weeks, which should be interesting.
I’ve started using a minimalistic “kids IDE” I threw together for teaching the kids. It’s helped us not worry about details like filepaths, saving, and running in a terminal. It’s not a great tool, but good enough. I’ve included links to better tools in the README, though this fits my needs for the moment.
I’ve set up AppVeyor to upload Windows executables to S3:
You’ll also need to install Stack.
This morning, Eliezer and Gavriella both had their next “official” lesson. I started over with that pattern matching demonstration. First, I showed them this example:
main = do sayNickname "Eliezer" sayNickname "Gavriella" sayNickname "Yakov" sayNickname "Lavi" sayNickname realname = putStrLn (nickname realname) nickname "Eliezer" = "Elie Belly" nickname "Gavriella" = "Galla Galla" nickname "Lavi" = "Fat baby" nickname "Yakov" = "Koko"
They got the basic idea of this. (And I ended up introducing
putStrLn around here as well, which they were fine with.) However,
as I had them typing out some of this on their own, they ended up with
a lot of trouble around capitalization, which was a good introduction
to Haskell’s rules around that. We’ll see how in a bit.
After establishing that we could pattern match on strings, I switched the code to this:
main = do sayNickname 1 sayNickname 2 sayNickname 3 sayNickname 4 sayNickname realname = putStrLn (nickname realname) nickname 1 = "Elie Belly" nickname "Gavriella" = "Galla Galla" nickname "Lavi" = "Fat baby" nickname "Yakov" = "Koko"
And gave them the challenge to rewrite
nickname so that the code
worked, which wasn’t too much of a problem. The misordering of
nickname did cause some confusion,
and then helped them understand better how pattern matching works. (I
didn’t intentionally put that in, it somehow slipped in while the kids
were working on rewriting things.)
I asked them what the type of
nickname was, and they said function
(yay!). And then explained to them that you can tell Haskell
explicitly what a thing’s type is, and typed this in:
nickname :: ? -> ?
The funny syntax didn’t give them too much trouble, and then we got to fill in the question marks. I asked them what the output of the function was, pointing at the string. I was surprised: they said the type was “nickname” or “name.” They accepted that it was a string, but they didn’t like that. (New theory: humans’ brains are naturally strongly typed.)
I then asked what the input was, and they said “number.” I hadn’t
taught them about
Int yet, and didn’t know about integers from math,
so I told them that integer is a kind of number, and that in Haskell
we call it
Int. Filling in the type signature was fine.
I pointed out that some things (like
String) were upper
case, and some were lower case. I pointed out that concrete things
that “actually exist” (maybe not the best terminology) are
capitalized. We know what an
Int is, for example. Variables are
things we don’t know yet, and those are lowercase. And finally, you
can put whatever you want inside a string, but it has to match
exactly. That seemed to click fairly well for them.
I pointed out that referring to the kids as numbers isn’t good. (He
responded that I actually do call them 1, 2, 4, and 8 sometimes…)
Anyway, I said that a better type for the
nickname function would be
to take a
Child as input. He said “can we say
Child = Int,” which
was a great insight. I showed up that, yes, we can do
type Child = Int, but that there’s a better way.
I introduced the idea that
data defines a new datatype, and then
data Child = Eliezer | Gavriella | Yakov | Lavi
Gavriella asked “what are those lines?” and I explained they mean “or.” Therefore, a child is Eliezer, or Gavriella, or Yakov, or Lavi. They got this.
Exercise: fix the following program, uncommenting the lines in
main = do sayNickname Eliezer --sayNickname Gavriella --sayNickname Yakov --sayNickname Lavi sayNickname realname = putStrLn (nickname realname) data Child = Eliezer | Gavriella | Yakov | Lavi nickname :: Child -> String nickname Eliezer = "Elie Belly" --nickname 2 = "Galla Galla" --nickname 3 = "Fat baby" --nickname 4 = "Koko"
Some caveats where they got stuck:
Lavi. I got to explain how pattern matching a variable works, and “wildcards.” They got this, though still needed to be coached on it.
lavihelped Gavriella realize her mistake. Syntax highlighting is a good thing here.
I started to try and define
data Person = Kid Child | Adult Parent,
but realized quickly it was too complicated for now and aborted.
I did not teach this well, the children did not get the concepts correctly. However, they did understand the code as I wrote it.
main = print total total = addUp 10 addUp 10 = 10 + addUp 9 addUp 9 = 9 + addUp 8 addUp 8 = 8 + addUp 7 addUp 7 = 7 + addUp 6 addUp 6 = 6 + addUp 5 addUp 5 = 5 + addUp 4 addUp 4 = 4 + addUp 3 addUp 3 = 3 + addUp 2 addUp 2 = 2 + addUp 1 addUp 1 = 1 + addUp 0 addUp 0 = 0
They really hated how repetitive typing that out was, which was
exactly the goal. It was easy to convince them that this was stupid. I
addUp 11 and it broke. They understood why,
and so we started working on something better.
main = print total total = addUp 10 addUp 0 = 0 addUp x = x + addUp (x - 1)
I stepped through the executable of this, complete with pattern matching. Doing this a few times in the future is probably a good idea.
Eliezer asked what happens if we remove the
addUp 0 = 0 case. We
discussed it, he said it wouldn’t work, and said it would “keep
going.” I told him it was called an infinite loop, and we got a stack
overflow. Good insight.
Gavriella asked how long it took me to learn this stuff. I told her 12 years; after all, I only started learning Haskell in my twenties. It made them feel pretty good that they were learning this stuff earlier than I did.
I gave them an exercise to implement
multTo instead of
didn’t understand this, or recursion, and I had to help them through
the whole thing. Mea culpa completely.
Gavriella asked for another exercise:
main = do sayAge Eliezer sayAge Gavriella sayAge Yakov sayAge Lavi data Child = ... sayAge child = print (age child) age ? = ?
She typed something like:
age ? = ? Eliezer=10
I showed her that she needed:
age Eliezer = 10
And then she got the rest just fine, though with a few capitalization mistakes.
Since the kids have been back at school and we’ve been busy with work and some home renovations, I unfortunately haven’t had a chance to continue much with the kids coding training. However, when discussing the general topic of education at Functional Conf, the topic of “the function game” came up, and I wanted to share what we did. I found this a great—and perhaps vital—pre-training for Haskell.
The function game is simple: I pretend to be a function, let the kids give me input, and I give them output. They need to try to figure out what the function is. Here’s an example conversation:
Me: When you give me 1, I give you 2. When you give me 2, I give you 3.
Them: What if I give you 5?
Them: You’re just adding 1!
With each kid, I’ve always started off with addition. I’ll later get into identity, but that one is (perhaps surprisingly) more confusing for them. Multiplication by 2 is a good follow-up to addition. Then, when they get comfortable with discovering a few of these, I’ll throw in:
f x = x * 2 + 3
f x = x(identity)
f x = 5(constant)
We’ll typically play this game at the dinner table. At some point I’ll also actually define a function for them, after they’ve already experienced some success at guessing what I’m doing:
It always gives the same output for the same input
The next curveball I introduce is different types:
Me: When you give me “apple”, I give you 5
Me: Functions don’t just work on numbers
Me: When you give me “book”, I give you 4
Them: It’s the number of letters!
After that, each time I tell them I have a new function, they’ll ask me the type of the input. As a Haskeller father, I couldn’t be more proud :).
I’ll also teach them about functions of multiple inputs:
Me: When you give me 2 and 3, I give you 5
Me: Functions can take more than 1 input.
And after a few more examples, they figure out that I’m just doing addition.
I think I tried demonstrating partial function application by saying “when you give me 2, I give you a new function you can play with.” But I don’t remember if I actually did this, or if I just planned it. And since I’m sitting in a hotel lobby waiting for a cab, I can’t test out the theory on them right now.
Anyway, I hope this proves useful for others trying to teach their kids (or maybe non-kids!) either math or functional programming. If you try it out, please let me know how it goes.
Previous lessons have all been focused on teaching our ten and eight year olds some coding, since our six year old (Yakov) is still working on reading and writing in English. However, Yakov’s been home sick all week, and he asked me today to teach him some programming. So I came up with a simplified version focused solely on the GHCi prompt.
I started off with:
> x = 32 > x + x
And then I asked him what he thought the answer would be. He quickly came back with 64. Then I typed in:
> y = 5 > x + y
And he got 37 pretty quickly. We played around with a few more bits of simple arithmetic (he hasn’t really mastered multiplication yet). I introduced defining variables in terms of other variables:
> x = 2 * 3 > y = x * 2 > y + 3
This took him a bit longer, but entirely due to the multiplication! This showed me a few things:
Finally, I decided to push things just a bit further and introduce functions:
> f x = x + 3 > f 7
This confused him at first, but once I explained that this was
applying the function
f to the number 7, he got it, and said “oh,
it’s the +3 function.” (Remember from last
that he’s been playing the function game for a while.) Next I hit:
> x = f 0 > f x
This was easy: it’s 6! Finally I gave him two more challenging ones:
> f (f 0) > f (f (f 10))
I fully expected confusion about parentheses. I was shocked: he wasn’t bothered by them at all. He immediately got both answers, and was very proud of himself.
Total time: less than 10 minutes, probably closer to 5. Which is good, because he’s got a short attention span and wanted to play some Nintendo with me too. Overall, I was very happy with how many concepts he was able to absorb.
(Thanks to my
~/.ghc/ghci_history file for reminding me what we