Beginners’ Programming Environments, and Haskell

Greenfoot is a beginner’s programming environment for ages 13 upwards. It is a programming framework and IDE for Java targeted towards games and simulations: anything where the main output is 2D graphics and sound. Here’s some screenshots:

Greenfoot has several nice aspects (disclaimer: I’m a developer on the Greenfoot project). One is that the visual setting allows instant feedback on what your code is doing. You write a line of code to get your crab moving across the screen, and two clicks later your code is running and the crab is moving around. This sort of instant and obvious feedback is very useful when you are beginning programming. (Actually, it’s always useful — but we have to learn to do without as we progress to programming more complicated systems!) You can invoke methods on objects by right-clicking on the object and selecting the method from a pop-up menu, which is a little bit like a graphical version of a REPL. So if you are given a scenario with some pre-coded behaviour, you can interact with the objects before even getting to the code.

Another nice aspect of Greenfoot is that it embodies object-orientation well. Not everyone (and probably especially not Haskell programmers) is sold on OOP, but set that aside for this post. The object/class divide is immediately present: Wombat is a class, you can create many Wombat objects in the world that have the same fields and same behaviour, but different state. This is obvious from them running around separately: the concept comes across implicitly rather than having to be taught too explicitly, because of the design of the system.

A while ago people used to scoff at Java, declaring “public static void main(String[] args)” as a reason to stay away: too much is revealed upfront before the students can start programming. Incidentally, one could equally apply the same criticism to Haskell: the monadic type of the main function forces beginners to confront monads immediately. Greenfoot and other frameworks have shown this is easily dealt with. You will never see the main method in Greenfoot, and care is taken to steer beginners away from blank screens or from having to learn too much before they can get going. Writing Hello World starting with a blank file takes at least seven lines of Java code to print out a line of text, but adding one line of Java code in your first part-filled Greenfoot scenario makes a crab run across the screen. It isn’t the language that makes the difference for how hard it is to get started, it’s the framework.

The original designers of Greenfoot wanted to teach OOP to beginners, so they found a way to get across the key concepts of OOP easily, put it in a setting (interactive games) that can attract young beginners and produced the software (and supporting teaching material). Idea times execution.

What about Haskell?

This leads to my question: if a beginner’s environment can be produced for OOP and Java, surely it can be done for functional programming: specifically, Haskell. (The same question applies to the process-oriented programming model that CHP espouses — I believe the answer there is a clear yes, with a relatively close correspondence to the OOP design of Greenfoot, so it’s a bit less interesting to discuss.) Let’s toy with a potential design for a Greenfoot-like system in Haskell: that is, an interactive, graphics-based environment for beginner Haskell programmers. I’ll give our hypothetical system the name StartHaskell, just to make referring to it easier for the rest of the post.

First of all, what are the key concepts we would like people to learn in Haskell at the beginning? Function calls are central: without understanding functions and function calls, you can’t do anything in Haskell. Types are quite important, too — especially since they are bound to be the second class of error you encounter (your first will be a syntax error). These are very obvious: functions are as fundamental to functional programming as variables are to imperative programming, and types are very important in functional programming.

A key and distinguishing feature of Haskell is the notion of a monad. Different people have different opinions on monads. Is it best to introduce them via a simple monad like Maybe or State, or is it better to use the IO monad? A myriad of monad tutorials on the Internet provide all the different answers you can imagine. The IO monad is the one that does tend to push monads in the programmer’s path sooner rather than later: the main function has a monadic IO type. Should this be hidden by providing a main function that calls the programmer’s pure functions, or should it just be exposed straight away? Whether or not you expose monads is closely tied to the API design of StartHaskell.

API Designs

The notion of API in a programming environment like Greenfoot or StartHaskell actually swings both ways. There is the API that the StartHaskell system provides to the programmer, which will include things like functions dealing with images. But there is also the API that the programmer implements for the StartHaskell system — which is mainly what I want to discuss (and which has a heavy influence on the first kind, the system-provided API). In Greenfoot, every active thing in the world inherits from an Actor super-class, which has an act() method that the programmer implements to provide the functionality of that actor. These act() methods are called once each frame by the Greenfoot system: this is the API that the programmer implements. Since our hypothetical StartHaskell system will be functional rather than object-oriented, we will likely have one function to provide the functionality for the whole system (called once per frame) rather than bundling the functionality with individual objects. So let’s explore some different designs.

Option #1: Avoid monads entirely

We could focus entirely on pure functions. Conal Elliott’s picture work comes to my mind — and Simon Thompson’s Haskell book uses pure picture-based examples from the outset (using lists of lists of pixels, rather than Conal’s more abstract approach). You could conceive of a graphical framework that perhaps used a function from input-set to output-picture, i.e. step :: (KeyboardInput, MouseInput) -> Picture that would then be called each frame to produce an output picture given the input. Persistent state — that is, state that persists between frames, such as the position of the player in the world — is needed, so you might allow state to be returned as a result and passed to the next invocation, i.e. step :: (KeyboardInput, MouseInput) -> a -> (Picture, a). You could then provide various combinators for composing pictures that the programmer can use. (An approach based on Functional Reactive Programming, aka FRP, might also be possible — I don’t know enough FRP to talk about this, but feel free to write your own blog post explaining how FRP is the way to go!)

Option #2: Have a pure/monad split

Kodu is a new, very polished system for young programmers that uses a visual approach based around “when/do” rules. The “when” part specifies a condition that constrains when the rule is run, and the “do” aspect specifies the actions to take when the condition is satisfied. This easily translates into an API such as: rule :: (Environment -> Bool, SomeMonad ()). Different rules could be combined via combinators, or by changing that API into a list of rules. (One problem I have with lists of these rule-based interfaces is precedence: when one rule fires, does that preclude all other eligible rules firing? Or do they all fire together — and if so, in what order?) The system could then provide functions for accessing the environment data-type, and various monadic actions for affecting the system, such as drawing. Persistent state would probably be handled by making the SomeMonad a state monad.

Option #3: Introduce monads immediately, using do-notation

A lot of people try to understand monads by looking at the type signatures and going from there. If you completely ignore how monads are implemented, and just look at do-notation, many monads (especially the IO monad) are very close to imperative programming, and thus this is one way to introduce them. Haskell is a very nice imperative language! In this case we might require the user to provide an API like step :: SomeMonad (), or to make persistent state more apparent, step :: a -> SomeMonad a. The system would then provide an API with monadic functions to ask about the world, and to affect the world.

Option #4: Don’t Call Me, I’ll Call You

In all my options above I have assumed that the StartHaskell system will call the programmer’s function each frame. This design is taken from Greenfoot, and has various benefits. One is that if the programmer’s function is empty, the surrounding system will still be rendering frames and receiving input. The interact function in Haskell has this same flavour: here’s a function that contains my desired behaviour, you handle the heavy lifting of doing the IO and call me at the appropriate point. One problem with being called by the system is state. A graphical interactive program such as those in Greenfoot (e.g. games) will tend to maintain state: the positions of things in the world and the state of the player. I mainly dealt with that above by allowing the programmer to return some state that will be passed back in next frame. I’ve waved my hand slightly over the type of the state: each program will have a different type for the state, but that shouldn’t be hard to deal with in the environment.

An alternative approach is to take control: I’ll write my own code to drive the program and will call you at the appropriate points. There could be a single call into the framework, like nextFrame :: SomeMonad (), that does everything necessary: waits until the next frame is due, renders it, reads all the input devices and so on. So the most basic program would be forever nextFrame. Not too heavy to get started. Then you could pass state around yourself more easily instead of having to return it to the StartHaskell environment to be passed back to you later.

API Summary

I don’t know which of the above options is best — there may be other better options that I haven’t considered. What I do know is that it is surprisingly important. What kind of API you ask the programmer to implement will have a big effect on the style of programming that they learn, and on the concepts that they will have to master. This is particularly true in languages where multiple styles of programming are supported, like Haskell or C++, whereas Java only really has one main style of programming.

Environment

Greenfoot is not just a software library/framework for graphics. It is a complete programming environment, including interactive invocation, debugging, buttons for compiling and running the program (with no build scripts in sight), and a source code editor. Having a complete environment helps beginners a lot. You do not want beginners who you are teaching any programming language to learn the compiler command and flags on day one: that can come later, the point is to teach them programming! It is also important to offer a simple environment: giving Eclipse to someone who hasn’t programmed before is just unnecessarily complicating their learning process. A beginner’s environment should be simpler than that of a professional programmer.

Editor/Compiler

I think one major obstacle in Haskell is likely to be its error messages, particularly those for types. (Is every error message in Haskell one of: a syntax error, a type error, or an unknown identifier?) There has been work done before, for example in Helium, to try to improve this for Haskell. A better approach to teaching functional programming as a whole might be to use a language with a less powerful type system that allows for simpler errors, but I’m constraining myself to use Haskell for this post. So if you do have to teach Haskell, how can you best help with type errors?

Here is a mock-up of something that I always thought would be useful for type errors:

The idea being that those call-outs appear one-by-one as you move through the source. I like it because it visualises the process that I go through in my head when I encounter a type error, and the computer can doubtless do it faster than me. However, it is always worth bearing in my mind that what is helpful for an expert (as I guess I am in Haskell) is not necessarily helpful for a beginner. You need some level of understanding of type inference to work with that representation, and if a beginner doesn’t have that understanding then the interface may only serve to confuse.

Thoughts on Teaching Programming

I am a beginner when it comes to teaching and educational research, but here’s a few thoughts I’ve picked up so far:

Consider the following as a guiding principle. There are only two reasons that things are hard to teach: what you are trying to teach needs to be improved (e.g. it needs more research), or you haven’t found a good way to present it. (Warning: this is an unfalsifiable statement!) Recursion is meant to be hard, but there are many who believe recursion (particularly structural recursion) is easy when taught right. Objects were meant to be hard, but I think objects-first works quite well at getting OO concepts across. Concurrency can be taught to beginners when it’s a good model (occam in combination with robotics has proved easy to teach to beginners). And so on and so on.

Beginners can benefit from tools that are specialised to beginners. It is helpful if both beginners and experts realise this! Beginners shouldn’t start with Eclipse because they’ve heard that’s what Real Developers(TM) use, and experts should not pour scorn on beginners’ tools because they are too simple or hide intricate detail — that’s the point!

None of what I have said here about teaching is new, and much of the advice here is already followed on many courses. Most teachers give out partially-complete code to avoid students beginning with a blank screen, and it’s well-known that the key to teaching is to find good examples and present the concepts in the right way: it’s much easier said than done! My main intention was to think about what an equivalent to Greenfoot for functional programming would be like. It may be that an interactive graphics-based setting is not a good fit for a first functional programming environment. Perhaps there are other alternative settings that could be used — but it I think it should be something with a more widely appealing end result than calculating the fibonacci numbers!

Like this:

Related

I like where this is going although I would not try to make something like Greenfoot. Haskell is complicated enough to make experienced developers like myself feel stupid and rip my hair out in frustration. It took me weeks it understand what the IO Monad was and how it worked and most of that time I thought it was the most retarded thing I had ever heard of.

Why would you infect a type with IO? Why don’t I just create explicit transitions between pure and unpure code. In addition many of the examples are so contrived as to be useless.

Now a few months later the problem that this post is hinting at is that haskell’s tools are just not very good. F# has its issues but in my opinion its tool chain is vastly superior. I have hope for being able to leverage the LLVM stuff in GHC. I think that will improve the situation.

I think haskell should focus on what it is good at. Building an environment for beginners would be more useful but I would not have it focused on graphics…but rather data processing. After all that is why many of us are using haskell.

The point I am trying to make is that we should try to find a way to present Haskell (potentially, including monads) that makes it easier to understand the IO monad and similar. A lot of people start out pure and then try to extend their understanding to monads and have trouble, as you did. I’m wondering whether, say, a monads-first approach that begins with do-notation as the first piece of Haskell you see might be a better way of approaching the teaching of Haskell.

I’m not saying Haskell’s tools for experts are a problem (although that might also be true), I’m just proposing that there is space for new beginners’ tools. You might be right that data processing is better than graphics, but I think that’s more of a struggle to make it immediately interesting. One tip I saw recently is to use the UK’s new open data initiative so that the data being worked on is actually interesting and the things you are working out are real and potentially new.

I disagree with Steve – I think graphics is a great starting point. It has a good background in this kind of tutorial setting, and I don’t see any reason why Haskell is unsuitable for this?

One issue I tend to have with these kinds of beginner environments though is that they tend to be rather restrictive: In order to simplify things they tend to be restrictive.
One very attractive element of programming is teh POWAR!1. If all I can do is move crabs about, I’ll quickly get bored. If there’s the possibility that if I study really hard and really work at it I can create a AAA game where I can p0wn all my noob mates, I’m likely to be far more motivated.

I see (and occasionally encounter) a fair number of kids finding C/C++ too hard a starting point, but desperately trying to learn to code games. Games are a serious motivator for them (as they were for me). DarkBasic, XNA and other environments are pretty popular here, but by no means perfect. I think they miss a lot of tricks and (XNA particularly) are still pretty goddamn complicated.

Now, I’m personally very fond of Processing. It’s designed for visual artists, but has scope well beyond that as a general purpose graphical hacking tool. It’s simple enough to pick up very quickly, and powerful enough to write some pretty impressive stuff. Powerful enough that it gets a surprising amount of use at my office (Geomerics) as an extremely quick prototyping environment.

For a while now, I’ve wanted to create a Haskell version of Processing and fix a few things. With the right set of ingredients I think this would be attractive to both beginners and experts alike, as the original Processing does so well. Anyway, this is the direction I’d take a beginners tool. :)

Regarding monads, I feel the syntax does more to hurt than help beginners. Even now I tend to avoid it. I use a lot of >=> which I think helps, and is simpler to understand once you can use . effectively.

I agree with Sam that games are a serious motivator for young beginners.

Are you familiar with the work of Michela Petroni, Bertrand Meyer and others at ETH Zurich? They teach the first-year programming course using a pre-built graphical program, that the students have to modify in (initially small) some ways. There is a book around it called Touch of Class (it’s using Eiffel).