In short: CodeWorld has been extended with a new feature that makes it easy to import code across different projects and users. I discuss why I added this capability, how to use the feature, and what you can do with it.
Over eight years of developing and teaching mathematics and functional programming with CodeWorld, I have ruthlessly insisted on keeping it simple. To this end, I’ve discarded even the things that come standard with most programming languages — among them, a story for modularity and code reuse. CodeWorld is used for short exercises, to play around, to write a few dozen lines of code and see a result. It has never been a serious tool for programming in the large. One project is one text document, and it runs by itself. This simple rule keeps the UI clean and students’ mental model very simple.
But it hasn’t always been a bed of roses. Here, I want to talk about three challenges that have come up in the past:
- Students have learned to do a lot of copy and paste, to reuse their drawings in new projects. This initially didn’t bother me much, since I’m not teaching software engineering practices. However, it encourages students to write complex expressions in a single top-level definition so that it’s easier to copy around. That’s not a good strategy.
- Teachers have wanted to provide scaffolding and setup for assignments and classes, but struggled with their students copying boilerplate code. Fernando Alegre solved this by contributing modules of extra functionality to CodeWorld itself. But other projects, such as setting up virtual physics labs or biology simulations, have been abandoned.
- Haskell community members have found creative uses for CodeWorld beyond the classroom, and have struggled with the limitation. Joachim Breitner even created a horrifying (but awesome) tool to help him write code in multiple files and then mush it all together in a single file to run inside CodeWorld, and used this to create the presentation for a talk at ICFP in 2017.
Clearly, there’s an opportunity to do better. And, indeed, one of the first issues in the CodeWorld bug tracker was for some kind of library project that students and teachers could use to define reusable components. Library projects, though, turned out to be a bad idea. Having two project types is quite complex for the children who are the primary users, and trying to share code with other students is even harder. How do we resolve names, or handle versioning?
Introducing: Import by Hash
The solution to all of this was to piggyback on the existing sharing system in CodeWorld, and allow projects to be imported by hash.
The way this works is simple. First, you create your original code in its own project. Now run it. Now you’ll see a hash added to the URL, beginning with the letter ‘P’. You can just import this into another project, and any symbols exported from the module are now available in your other projects.
There are a few subtleties to keep in mind that might be surprising at first.
- You can include a module header, if you choose, and it is useful for controlling exports. However, the module is imported by its hash rather than its module name, so the module name itself doesn’t matter.
- Even if a project exists only to define things for reuse, it should compile and run on its own. So go ahead and use the main entry point (remember, that’s
programin the educational dialect, or
mainin plain Haskell) to define a test or demo. These are not exported (or rather, they are exported but the name is mangled), because they would conflict with the entry point of the code that imports them.
- Don’t go too crazy! While this does make organizing your code easier, it’s also easy to end up with long chains of dependencies, with the result that you might build a lot of code every time you compile a short file and your builds may time out.
- If you give students your program hashes (beginning with “P”), they can also use those to get at the code you’ve written. Sometimes that’s not what you want. For instance, if your import is a unit test used for grading, you may want to hide exactly which test cases you’re checking. So you can also import deployed hashes, which have the same behavior, but cannot be used to access source code. To get a deployed hash, run your code, click Share, and then “Share Without Code”. You can copy the
dhashparameter from that URL, and import it as a module.
What do I do with this?
Some of the use cases for import-by-hash in CodeWorld were mentioned above. As a student or self-motivated learner, you can easily import and reuse things you’ve defined in other projects. As a teacher or blogger, you can give your students/readers a link or import statement that they can use to get access to setup or scaffolding without too much boilerplate. But the use case I’m most interested in is to build and collect sets of useful educational environments.
Several years ago at TFPIE, I learned about Scott Walck’s library for letting students set up quantum physics experiments on light beams in a Haskell DSL. His library is available on Hackage. Today, Scott’s students have to set up a Haskell compiler, and interact with it via GHCi or a command line that shows the final observation. They cannot see the experiment so easily, and may not realize if they’ve described the setup incorrectly. Now, though, a dedicated teacher could write a graphical subset of Scott’s library in CodeWorld, and hand out an import statement for students to add to their code and watch these light-beam experiments take place, observe the configuration of lasers and filters, etc.
This is, of course, not the only opportunity. There’s room for designing easy-to-use programmatic environments to explore classical mechanics, circuits, ecology, probability, finance, AI and simple neural nets… the possibilities are endless. The DSL gives students a flexible way to try out complex and structured systems easily, while the web-based environment makes it low setup, and the graphics keeps it visual and interesting.
The significance of this change is that before, doing something like this would have meant sending a pull request to the CodeWorld project and convincing me it’s important enough to add to my public API. It is now as easy as just writing the code. The results can be shared via class notes, blogs, YouTube, etc., and doesn’t need to go through any central gatekeeper!
I hope you play around with this and that we find it leads to exciting places.