Haskell for Kids: Week 7
Where We Are
This is the summary of week 7 of my Haskell class for children, aimed at ages 11 through 13 or so. You can go back and review the previous weeks to catch up.
We’ve now spent six weeks learning some of the Haskell programming language, and making various pictures, from simple stuff to some pretty complicated drawings. Now it’s time for the next big step: animations!
Using gloss-web for Animations
We’ve done all of our programming using the gloss-web web site at http://dac4.designacourse.com:8000, where you can program and look at the result in a web browser. So far, everything we’ve done has been in the “Picture” portion of the web site, which you get to by clicking the picture button:
Now we’re ready to move on to the next type of program. So to get started, click the “Animate” button instead.
This page will look very nearly the same, but don’t be fooled! The web site now expects from you a different kind of program: one that describes a picture that moves over time.
You’ll remember that the picture mode expected you write a definition for a variable called picture. Everything else in your program was just there to help you define what picture meant, and when you finished, the program would run by looking up the definition of picture, and drawing it. Your other definitions were useful only because they let you use those new words to define picture more easily.
The same thing is going on here, but there are two changes:
- Instead of picture, you’re defining a word called animation.
- Instead of being a description of a picture, animation is a function. Like all functions, it has a parameter, which is the amount of time that’s passed.
Warning: I’ve said from the beginning not to use Internet Explorer for the programming web site. It’s probably worked so far if you didn’t listen to me. But now it will not work any more, so you’ll need to use something else: Firefox, Chrome, Safari, Opera… take your pick, but Internet Explorer won’t work.
Let’s jump in with some examples.
Example 1: Rotating a square
The first example program we’ll write is the one that is given to you by the web site:
animation t = rotate (60 * t) (rectangleSolid 100 100)
Here’s how to think about what that means.Think of t as standing for the number of seconds it’s been since you clicked the Run button.
At the very beginning of your animation, it’s been 0 seconds since you pressed Run. So the program is saying the same thing as: rotate (60*0) (rectangleSolid 100 100). Remember that * means multiplication. Of course, 60 times 0 is 0, so this is just rotate 0 (rectangleSolid 100 100). It draws a rectangle that hasn’t been rotated at all.
But then some time passes. After half of a second, now t is 0.5. Now the picture your program is drawing is rotate (60*0.5) (rectangleSolid 100 100). What is 60 times one half? It’s 30. So now the picture is rotate 30 (rectangleSolid 100 100), and it will draw a rectangle rotated by 30 degrees. This will continue, too. After a full second, t is 1, and 60 times 1 is 60, so the rectangle will be rotated by 60 degrees. After 2 seconds, it will be rotated by 60 times 2, or 120 degrees. As t gets bigger, the rectangle will be rotated more.
Okay, here’s a little quiz: how long will it take for the rectangle to turn all the way around, a full 360 degrees? That shouldn’t be too hard: you want to find the number that will give 360 when it’s multiplied by 60. That’s how many seconds it will take.
What about after that? Can you rotate something more than 360 degrees? Sure, you can… but you can’t tell that you did it! Rotating 360 degrees looks just like leaving it alone. So, for example, when it’s rotated 390 degrees, that’s the same as just rotating 30. (If you were thinking about this, you might have noticed that you actually can’t even tell if a square has been rotated 90 degrees! That’s because of the symmetry of the square. But you can’t tell if any shape has been rotated 360 degrees, no matter if it has symmetry or not.)
Try this example on the web site, and make sure it looks the way you expected.
Example 2: Changing the speed
Let’s make a small modification to the example earlier. We’ll make the square rotate faster:
animation t = rotate (100 * t) (rectangleWire 100 100)
Just like before, t stands for the number of seconds since you clicked Run. At the very beginning, t is zero, and zero times 100 is still zero. So it starts in the same place. But now look what happens: after half a second, it’s rotated by 50 degrees. After a full second, it’s rotated by 100 degrees. It will only take about three and a half seconds to make a full turn.
Example 3: Starting at a different angle
Now let’s try to change the angle it starts at, so that it started standing on one point like a diamond.
animation t = rotate (100 * t + 45) (rectangleWire 100 100)
Okay, what’s that when t is zero? It’s rotate 45 (rectangleWire 100 100). So the square will start out turned on one point. It will still rotate at the same speed as before, though: 100 degress every second. Try that and see what it looks like.
Example 4: Moving
Now let’s try moving something instead of rotating it.
animation t = translate (50 * t) 0 (circle 25)
Take a moment and try to guess what that will look like.
To figure it out, let’s look at the picture we’ll get at different times. When the animation first starts, it’s been 0 seconds, and t is 0. Then this is translate 0 0 (circle 25). That won’t move the circle at all, so it will still be in the middle of the box. But now when t is 1 (after the animation has been going for a full second), it will become translate 50 0 (circle 25), so the circle will be a bit off to the right. In fact, the circle keeps moving right until it runs right off the screen (and even afterward, but then you can’t see it any more).
Example 5: Moving faster
Can you guess how to make the circle move faster? If you guessed that we want to multiply t by a higher number, you’re right!
animation t = translate (100 * t) 0 (circle 25)
The circle will still start in the middle. (Why?) But now after one second, it will have moved twice as far. It’s moving faster.
We won’t do it as an example, but what if you wanted the circle to start on the left side of the screen? Hint: it works a lot like example 3!
Example 6: Changing the size
What if you want something to get larger as time goes on? You can do that, too. Here’s an example:
animation t = circle (20 * t)
Once again, t stands for the number of seconds since you pressed Run. At the very beginning, your picture will be circle (20 * 0), which is the same thing as circle 0. A circle with a radius of 0 is too small to see, so you won’t see anything. But after half a second, it becomes circle (20 * 0.5), which is circle 10, and you will see a circle with a radius of 10. The circle will keep getting larger: after ten seconds, it will be circle 200. Eventually, if you’re patient enough, it will grow too big to even show up on the screen.
Try that, and see if it does what you expected.
(You could have also written that using scale: animation t = scale t t (circle 20). Do you see why those two programs should do the same thing?)
Example 7: Rotating around another point
Sometimes we might want something to rotate, but around the middle of the whole picture (or part of a picture), and not just around its own middle. The trick is to move it (with translate), and then rotate the picture that you get as a result of that.
animation t = rotate (60 * t) (translate 100 0 (circleSolid 25))
See if you can work out what that will look like at different times, and then try it out and check yourself.
Animations and List Comprehensions and Functions
You might have noticed that a lot of the things we’re doing here seem very similar to what we did with list comprehensions. You’d be right!
Both times, we had variables, and we got different pictures because those variables got different values. The difference is that with list comprehensions, we put all of those different pictures together at the same time. Now we’re using different pictures at different times. But the idea is the same: you use variables to represent numbers that you don’t know yet, and then you can build versions of your picture where those variables mean something different.
We’ve actually used variables like that three times:
- When we defined functions (like awesome), the parameters got names, and you used those names in the function.
- In list comprehensions, you used the funny backward arrow: <-, to pick a name and arrange for it to get lots of different values.
- Now, in animations, you’re again defining a function: a special one, where the parameter is the time.
This is something that will keep happening. Variables are very important, so you should get used to seeing expressions that have variables in them, and doing something called “substitution”. That just means answering the question “If t is 5, what does this work out to?”
Top Down Design With Animations
With pictures, we worked on organizing our program by top down design: we started by saying what picture meant, but we sometimes used other words the computer doesn’t know yet. Then we went back in and filled in what those words meant. We just kept this up until the program was done. The nice thing about top-down design was this: you didn’t have to think about your entire program at once. You could just think about one piece at a time.
We’d like to keep doing top-down design with animations, too. There’s one thing we have to figure out, though, and that’s what to do with t. Here’s what we’ll do:
- When we’re defining a word, if its a piece of the picture that, just looking at that one piece, will have some movement, then we’ll make it a function, taking a parameter called t.
- When we’re using it later on, we’ll pass t for that parameter.
It’s important to keep these consistent: if you define a function with a parameter, then you also have to give it that parameter when you use the function. If you define a variable without a parameter, then you aren’t allowed to give it a parameter when you use it.
Let’s look at an example: here’s a program to draw a clock:
import Graphics.Gloss animation t = clock t clock t = pictures [ backPlate, minuteHand t, secondHand t ] backPlate = color (dark white) (circleSolid 250) minuteHand t = rotate (-0.1 * t) (line [(0,0), (0,250)]) secondHand t = rotate (-6 * t) (line [(0,0), (0,250)])
We’ll break it down piece by piece:
- First, we defined the animation function. When you’re working in animation mode, you always have to define this, and it always has the parameter t, that means how many seconds since it started. We say that the animation is a clock. The clock will have moving parts, so we pass along t.
- Since we gave the time, t, to clock earlier as a parameter, our definition of clock has to say it has a parameter, too. A clock consists of three parts: a back plate, a minute hand, and a second hand. The back plate does not move, so we don’t pass along t. But the minute hand and second hand do move, so we’ll be passing the time along to those.
- Next we define backPlate. Remember we didn’t give it a t parameter because it won’t be moving, so this is just a variable.
- Now we define minuteHand. We did give it a parameter, so now we have to say it gets a parameter. And just like in the earlier examples, we can use that parameter t to make it rotate. We want the minute hand to rotate 360 degrees every hour, and an hour is 3600 seconds. So that means we want it to move only a tenth of a degree every second. Also, we’d like it to move clockwise, so that number has to be negative. (Remember, positive rotations are counter-clockwise.)
- Finally, we define secondHand. It needs a parameter, too, so we give it the t parameter. The definition looks the same, but now we want it to rotate 360 degrees every minute (60 seconds). That works out to 6 degrees every second, and it’s still negative so that the rotation is clockwise.
Hopefully, that doesn’t look too bad! It’s just the same kind of top-down design we’ve been doing, except with some time thrown in to keep things moving. You have to ask yourself whether each part has movement in it or not, and you have to consistent about the answer.
This week’s assignment
The assignment we’re working on this week is to make a working model of the solar system. That is, put the sun in the middle, and the planets around it, and have them rotate around the sun. A few notes:
- It doesn’t need to be to scale. In fact, the planets are so far apart, and so small compared to the distance between them, that if you try to draw everything to scale, you won’t even be able to see it! So just pick what looks good.
- If you are ambitious, try to add at least one moon, and give Saturn its rings.The hint here is that moons and rings aren’t too hard, as long as you’re using top-down design. For example, you should have something called saturn, and instead of making it a circle, make it a pair of circles with one of them stretched a bit.
Now suppose you’re adding a moon to earth, you probably want a function called something like earthSystem that represents the earth and its moon together. Since the moon is moving around the earth, it should be a function and have a parameter called t. When you’re defining earthSystem, you’ll probably want even more variables called earth and moon. These will probably just be colored circles.
- Our class here in Colorado Springs actually all decided to make up their own solar system with different planet names. That’s fine, too. In fact, the computer doesn’t care what you name things.
Good luck! And feel free to ask in the comments for help.
Just for fun…
If you’re bored, try this animation. It’s a preview of what’s coming in the next couple weeks.
import Graphics.Gloss animation t = pictures [ jumprope t, translate (-210) 0 stickFigure, translate ( 210) 0 stickFigure, jumper t ] jumprope t = scale 1.75 (1.1 * sin t) rope rope = line [ (x, 100 * cos (0.015 * x)) | x <- [-100, -75 .. 100 ] ] stickFigure = pictures [ line [ (-35, -100), (0, -25) ], line [ ( 35, -100), (0, -25) ], line [ ( 0, -25), (0, 50) ], line [ (-35, 0), (0, 40) ], line [ ( 35, 0), (0, 40) ], translate 0 75 (circle 25) ] jumper t = translate 0 (max 0 (-50 * sin t)) stickFigure
That’s all for this week.