Where is it used?” Of course IDEs have tools to tell me these things, but I still found myself manually checking each variable.
I thought, perhaps I could represent a function without using intermediate variables simply by drawing a line from the place each piece of information is used to the place that the information came from.
The type of programming I was used to at the time was highly imperative, so the first things I tried to solve were conditional branches (“if” statements) and imperative loops.
I got hung up on loops for a while and I found several research papers online that had complicated ways of handling loops.
Then I realized I could just use recursion.
Imperative loops were totally unnecessary.
My early solution for conditional branches was to put some kind of gate blocking the way.
You can see this in the illustrations below.
Data would only be able to pass through the gate if it was triggered by the dotted line.
In these examples I conceived of a sort of gate that would only allow data through if it was triggered by a dotted line.
Fortunately, I eventually took a Comparative Programming Languages class in which I learned Haskell, a lazily evaluated programming language.
From this I realized that if I switched to lazy evaluation, conditional branches can be modeled just like normal functions.
Another construct down!The factorial function: factorial(n) = if n ≤ 1 then 1 else n * factorial(n – 1)Now I could represent pure functions in a very simple way.
I continued to doodle these programs for some time.
I got a job as a programmer and at one point I described my idea to a co-worker.
He thought it was silly and that it would never work.
That inspired me to build a proof of concept.
The factorial function implemented in my proof of concept.
One thing I noticed about programming in my language was how many of the details of normal programming languages I didn’t have to think about in it.
There was no need to implement intermediate variables.
If I wanted to use the same information twice, I could just make another connection to the same source.
I could build rich expressions without having to dissect them into individual statements assigning values to variables.
It also allowed me to sidestep a lot of the complications that come from using human-readable identifiers or names.
In most programming languages, you name something and then use it by spelling its name in exactly the same way again.
This comes with a lot of complications though.
Two different programmers could give two different things the same name, and as a result names have to be disambiguated using modules, namespaces, packages and import statements as well as shadowing and lexical scoping rules and other things like forward declarations or the order in which things are defined in a file.
In Nameless, the names you give definitions are never seen by the compiler or interpreter.
They’re considered documentation.
You can use these names to search for a particular definition but once you find it and select it you end up with a direct reference to that definition based on its true name which is an opaque random ID.
As a result of this, every definition knows exactly what definitions it depends on.
I’m working on a publishing system for this where, if you publish one definition, you publish all its dependencies as well, and each definition’s published ID is based on its content as well as the IDs of all the definitions it depends on.
This is the ultimate form of version pinning.
Also, it would allow programmers to easily reference any subset of an existing program without having to take the entire original project with them.
Another cool result of this is that definitions don’t need to have only one name.
I could give one definition a different name for every human language and enable people who speak different languages to collaborate on the same program together.
Names could be translated automatically in the UI with Google Translate and then native speakers could edit and vet them.
This was all fine and good for representing pure functions, but to make it generally useful I also needed a story for how you’d write imperative code where things happened in a particular order.
This sort of thing is generally needed if you want to make a game.
For a time, I thought I might have to go back to the gates and triggers design I’d originally come up with for conditional branches.
But then I came up with a strategy for this by using “prerequisite” inputs, illustrated below.
Question 1 is asked before Question 2I gave all stateful functions an extra input and output, marked with a semicolon (I chose the semicolon since semicolons are traditionally used for separating statements that must be executed in order in imperative languages, but I’m likely to represent it differently in the future).
These inputs and outputs do not carry any meaningful data, but if you ask for any output from a stateful function it will evaluate (and discard) its stateful input before it does anything else.
This is what it might look like to set up event handlers for a game.
The blue boxes are function pointers.
Another construct I wanted to have in my language was inline function definitions, also known as anonymous functions or lambdas.
There wasn’t an easy way to do loops in Nameless without manually implementing recursion, so I was very interested in adding a way to easily pass the loop body into higher order functions like map and reduce.
In simple self-contained cases you could just pass in a function pointer, but in practice you’re likely to need an inline function definition so the loop body can access data from the parent function.
An inline function definition being passed into “map”.
It’s even (pointlessly) reaching outside its own scope to access the 5 that lives in the surrounding function’s scope.
To run the anonymous function I created a “function pointer call” node.
It takes its shape based on the function interface definition it’s based on.
You pass in a function pointer and some inputs and it produces the function’s outputs.
The “map” function, implemented using “reduce”.
The “reduce” function.
With the creation of these inline functions definitions I was reaching the limits of what I could accomplish in this prototype.
I displayed the inline function definition nodes at a single fixed size, which meant there wasn’t a way to put one inside of another, and if you wanted to put a lot of code inside one it could be awkward to make it fit.
Also, asking the programmer to drag things around on a free-form canvas and manually find a way to make them look nice seemed like a big distraction from the pure experience of programming.
What I really wanted was for the computer to lay things out nicely automatically, in a perfectly consistent and repeatable way.
I knew this would involve some difficult algorithms, so I decided to start over in a language with a good type system and lots of compile time checks.
I also wanted immutability by default, since that is how the interpreter should work as well.
And I wanted to re-implement it in SVG since that would make it easier to click on the lines.
I chose to rewrite it in ReasonML.
This is what I came up with:The factorial function with automatic layoutNested inline function definitions know exactly how big they need to be.
It was a long journey getting to this, which I can describe in more detail later, and there’s a long way to go to make this new implementation as useful as the old one, but what I have so far is very promising.
The old implementation: repository, live demo.
The new implementation: repository, live demo.
If you’re interested in this project, join me on discord.