FunBlocks A functional block-based language for CodeWorld

Fun Blocks Prototype

After looking up the constituent technologies which will be used for the project, work on the initial prototype began. For the initial version, we want a simple user interface with which simple CodeWorld applications can be built. Thus we want a version where a user can:

  • Drag and drop blocks from the toolbox
  • Provide simple error feedback
  • Generate code for a valid CodeWorld application
  • Run the generated CodeWorld applications
  • Saving and loading

Some more advanced features which are omitted for this release are:

  • Support for functions
  • Support for lists
  • Advanced CodeWorld applications (animations, events)

Simply put, we want a visual block-based interface which can be used to build CodeWorld applications.

This project is educationally based. We want to introduce programming to younger students and make it easy for them to understand the concepts behind it. For more advanced projects students may use the regular CodeWorld text UI.

The initial prototype can be found at code.world/funblocks

Blockly

This project relies heavily on Blockly for the user interface. Blockly provides an editor where blocks can be dragged and dropped on a canvas. These blocks can then be interlocked with one another through a snapping behavior.

Blockly provides many features that make it easy to adapt to a new language. It even incorporates simple type checking, luckily, however, there is a project that improves Blockly’s type checking.

For the user-interface, the choice was made to keep the interface clean and simple. The idea is to have a simple toolbar on the left-side with blocks that can be dragged onto the canvas on the right.

Since the project uses Blockly, blocks can be snapped into each other. A block has a single output and may have multiple inputs, each having a type.

Translate Block

In the image above we can see a Translated block, taking a picture and two numbers, and outputs another picture that is translated according to the given x and y values.

Each block may have zero or more inputs and one output type. All blocks are typed, and the functions in CodeWorld work on immutable data structures. A function may take a color as an input to produce another modified color. This works well with a visual block interface and we can expressively see how the program flows.

Blockly provides a lot of features out of the box, though sometimes changes are necessary specifically for this project.

The largest has been to use Anthony’s, which provides some useful typing and polymorphic features though it also diverges from the main Blockly branch and is almost 300 commits behind.

Other changes have been required as well, such as:

  • implementing the let blocks
  • changing default methods of validation for numbers and function names
  • adding error highlighting

and other small changes and additions. Thus we maintain a separate fork of Blockly for this project.

CodeGen

When we build the program below:

Drawing of

we obtain the output code of:

main = drawingOf(rotated(colored(solidRectangle(7,5), white),(60 + 70) * 0.9) & solidCircle(4))

Which looks rather ugly, but code formatting will be addressed at a later stage.

Currently, the regular Blockly code generation paradigm is followed, where a code generation function is given for each block, that converts the block into code. For a single block, we convert all input blocks to code, which are then combined into a single block of code. This process is repeated for all top-level blocks.

For each block, a generation function is assigned, which looks something like this:

blockSector :: GeneratorFunction blockSector block = do startangle <- valueToCode block "STARTANGLE" CNone endangle <- valueToCode block "ENDANGLE" CNone radius <- valueToCode block "RADIUS" CNone return $ none $ "sector(" ++ startangle ++ "," ++ endangle ++ "," ++ radius ++ ")"

This approach has some disadvantages, such being:

  • Difficult to ensure code is consistent and that all types match.
  • Difficult to find errors when they occur
  • Having to define a block for every CodeWorld function

The first problem is alleviated with a different version of Blockly that supports polymorphic types. I’m using an updated version by Anthony that also support automatic coloring of Blocks. Hence the strategy currently taken is to prevent errors through the block based editor.

This version of Blockly fits this project great. Since we don’t have side-effects the type of the if-block should be of the same as the then or else expressions. In order to have a single if block, it must be polymorphic, and this is provided by the alternate Blockly.

If Text Above we can see the if block matching the Text type; below we can see the if block matching the Picture type. If Picture

Regardless, the generated code is then displayed and syntax highlighted using CodeMirror.

The generated code is sent to the CodeWorld server, which compiles the code into JavaScript. The compiled code is then run in the browser and displayed on an HTML canvas.

Preview

Error handling

First priority is to prevent a large class of errors from ever occurring. This is done by only allowing certain connections between blocks, validating input, providing default blocks and so on. This makes it easy for the user to construct valid programs.

However, errors do happen. What happens when a block contains a disconnected input ?

In this version, we visually indicate at which block the error occurs and display an error message for the block. This can be seen in the picture below.

Error

Some other errors, such as runtime errors will be addressed at a later stage. Luckily, we currently don’t have missing patterns or partial functions to check for.

Project Management

Project management falls in line with the way things are currently done with the regular CodeWorld site.

Blockly supports exporting the current workspace as XML. This XML is then sent to the server and a hash is returned. This hash identifies a CodeWorld FunBlocks application. For example code.world/funblocks#PN2GoG8W2OMugJrzTw0GKYA loads a drawing of a coordinate plane.

Google’s authentication API can be used to log in. Once a user is logged in he can then save and load his projects by name as well.

Issues / Problems

A large majority of the project is in JavaScript, this includes the Blockly library.

Originally I intended to keep the majority of the code in Haskell, however, this is difficult to do. Currently, there is a large part of additional code for Blockly and an additional JavaScript file that handles the user interface initialization and logic.

Most of the code that define the various types of Blocks are in a Haskell file. However, some more standard blocks such as the math number block (which included some validation) are modified from the regular Blockly code base. The let definition is also modified Blockly code.

Thus organisationally, similar features and code are split between the main project and the Blockly fork.

Another issue currently is the renaming of caller blocks for Let definitions. When a Let definition is renamed the callerb blocks should get renamed as well. This currently disabled as Blockly is sometimes calling the text input validator for the wrong block, which tries to rename all of foo’s blocks to bar.

The block type unification for the Let definition is also currently producing strange results and the issues on this are covered in the issue tracker.

Further Work

Currently, we are only able to build very simple programs. For more complicated applications such as animations, we require first class functions. We also need to cover more advanced data types. A starting point would be to cover lists and tuples.

For CodeWorld simulations, the user is required to supply his own world data type, and a user-friendly way is needed for the user to build these in the Blocks UI.

Some more advanced functional features such as pattern matching would make this easier, and it remains to be seen how this will be implemented.

Everything is currently on track, which might mean we will get to spend time on same of the more advanced features later.

GHCJS

Working on the project I’m exposed to GHCJS and JavaScript a lot and I really like what GHCJS is doing. JavaScript has a tendency to let anything almost work and I spend a lot of time debugging and finding JS errors when working on Blockly code.

Straight after starting out I decided to try and keep the bulk of the work in Haskell, which meant using GHCJS. The GHCJS ecosystem lacks good documentation and sometimes in order to accomplish things that are simple in JS, a lot of time has to be spent navigating through many files of GHCJS code and a bit of luck. One such is an example is when you want to assign a higher order function in JS:

Blockly.FunBlocks["block_number"] = function(block){...}

where we want to assign a function to do the code generation for the block indexed as “block_number”

Which involved looking at GHCJS callback source files , even though there are Stackoverflow questions regarding similar issues, the GHCJS project is fast moving and the libraries keep changing.

Haskell Summer of Code

I would like to say thanks for being part of this year’s Haskell Summer of Code as I’m enjoying working the project quite a bit. Thanks goes to all of those who made it possible as well.

I think the way the project is managed is quite good. We (me and my mentor, Chris Smith) are primarily communicating through emails and Github. Working on Github helps a lot and the issue system greatly helps manage the project. It helps when you want to have some idea or goal to work on or if you want to fix something quickly. It’s too easy to forget about a minor bug if it isn’t written down somewhere.

Chris has also been a great help testing new features, providing feedback, opening issues and reporting bugs (which sometimes are a lot, I apologize !) and providing overall direction for the project.