Then a couple of years ago I started to get interested in pure functional programming and in particular the programming language elm. The shift to start thinking functionally rather than imperatively definitely required some effort, however I quickly became addicted to the reduction in cognitive load from referential transparency and to the feeling of solidity from knowing my code had been successfully type checked.
At this point I started to believe that the only sensible approach to a general purpose visual programming language would be one that was pure and statically typed. And in the absence of any alternative I decided to experiment with making one! This only a demo, a proof of concept, but it can be used to make some simple web apps.The language/editor is currently written in elm, modelled on elm and compiles to elm. This means that if you are unfamiliar with elm, you may want to start by reading the guide there and will definitely want to refer to the functions in the core and html libraries.
Programs are developed by manipulating blocks on a canvas rather than text in an editor. Blocks can be dragged onto the canvas from a library on the right of the screen. Blocks basically represent functions and use arrows on the left of the block to represent inputs and arrows on the right of the block to represent output(s). You can then connect blocks by dragging from one block’s output to another block’s input. To create anything that will be visible on the screen you must have a ‘main’ block. The only things that can be connected to the main block are static html or a ‘Program’ for interactive apps. This is directly following the ‘elm architecture’ and you should read a little about it there if the examples don’t make sense.
|Add block from Library||Find the block you need in the library on the right of the screen then drag it (using the left mouse button) onto the canvas|
|Filter blocks in Library||The Library on the right of the screen has a text input at the top. By typing letters from the Module/Block name you can filter the library blocks to make it easier to find the block you need|
|Add block directly to the canvas||The quickest way to add blocks to your model is to left click on an empty area of the canvas where you want to place the block. After clicking a small red cross will be shown and you can start typing letters from the module/function. A filtered list of possible blocks will then appear just below where you are typing. Once you see the block you need you can click it to add it to the model|
|Connect blocks||To connect blocks you need to use the left mouse button to drag from one block's output to another block's input. At the moment you cannot drag from an input to an output.|
Currently the editor is completely dependent on using the mouse, but it would be interesting to add more touch/keyboard control in the future. Blocks can be selected in the editor by clicking on the name at the top of the block. When you do this the block will highlight in blue so you can see that it is selected. Blocks can be moved around by dragging the label at the top of the block to change its position relative to the rest of the blocks. If you click on an empty area of the model canvas any selected blocks will be unselected. By using the left mouse button to drag the canvas, you can move your view of the entire model.
This is a functional language and defining new functions is a core part of developing new programs. Function definitions blocks are purple and have an arrow in the label at the top. If you double click the body of a function definition block (not the label) the canvas will change to show the definition of the function (inside the function). If another function is definied inside the function, you can continue to double click to go deeper inside the nested functions. There is a breadcrumb in the top right of the canvas that shows where you are in the model. To navigate up/out of a function definition you double click on an empty area of the model canvas.
|Move block||Drag the block using the left mouse button to click on the label/text at the top of the block|
|Move canvas||The view of the entire model can be changed by using the left mouse button to drag any empty area of the canvas|
|Look inside function||To look inside a function definition you simply double click on the purple body section of the block|
|Move outside function||To exit from a function definition block (go up a level in the model hierarchy) double click on any empty area of the model canvas|
There are some additional controls available using the keyboard. Holding down the shift key and dragging across the canvas allows you to select multiple blocks at once. All the selected blocks can then be moved at the same time by dragging one of the labels of the selected blocks.
Some additional keyboard controls:
|Backspace||Delete any elements that are currently selected.|
|Ctrl+z||Undo the previous change to the model|
|Ctrl+y||Redo changes to the model|
|Ctrl+c||Copy the currently selected elements|
|Ctrl+x||Cut the currently selected elements|
|Ctrl+v||Paste elements from the clipboard. To control where the elements will be pasted, you can click on an empty part of the canvas before pasting|
There are a few menus available at the top left side of the canvas window. For the ones that open a dialog, just click outside the dialog to cancel/dismiss it.
|Preferences||Just has two options, the first shows the inferred type of all the ports in the model without having to hover the mouse over the port, the second shows the qualified name in the block labels (including the module name). The settings are saved in localStorage so should persist across visits to the site|
|Save||Allows you to choose a name and save your models to localStorage.|
|Models||Displays a list of the models saved in localStorage. The models can be loaded by clicking on the name or deleted by clicking on the cross on the right. There are also some demo programs that can be loaded from here|
|Reset||Clear the canvas and start again on a new model|
|Compile||Pressing the compile button will send the generated elm code to be compiled remotely. Once the code has been compiled the results will displayed in the "Html" tab. The elm code that was sent to the server can be seen in the code tab. If the model contains errors, the compile button will display the number of errors in the model and pressing the button will not compile the model but instead take you to each of the errors|
|Share||Clicking on the share button will send your model to the server and generate a link that can be sent to anyone you want to take a look at your model|
The language is statically typed and has type inference. For any block in the editor, you can examine its type by hovering the mouse over the input and output ports. If there is an error in the model somewhere it will be shown in a red label next to the port where the error has been found.
A valuable feature found in many functional languages is the ability to partially apply functions. In this visual language this is achieved by controlling the arity of the functions. Each function block has a "+" symbol at the bottom that can by used to increase the arity (number of inputs) and the last input has an "x" next to it that can be used to delete the input and reduce the arity of the function. As the number of inputs is varied the type of the function's output will change accordingly.
To do anything interesting we must be able to create new functions. In the Syntax section of the library on the right there is a purple block called "Function". Adding one of these blocks to your model creates a new user defined function. Any user defined functions will then be visible in the "User" section of the library on the right. The function name can be changed by altering the text in the label at the top of the function block. At the moment all functions are global and must therefore have a unique name.
To start defining the new function, double click on the purple body of the function block to look inside the function. Initially the function definition will contain only a single output block. You will typically then need to add some input blocks to help define the function. The input blocks have a numeric label to identify the order in which the input blocks apply. It is not necessary to add any input blocks if you just want to return a simple value, or define the function in a "point free" style.
To use a new function in your program there are two options. Firstly the function definition block itself can act directly as a call to the function. This is useful when the function does not need to be used multiple times and is primarily being used to organise/name bits of logic in your program. The second way of using a defined function is by dragging it from the "User" section of the library on the right into the model. This allows the function to be used as many times as needed within the program. It is also possible to create recursive functions by dragging the function block from the "User" section of the library into the inside of the function definition block.