by Eric Hosick (erichosick@interfacevision.com)
You can mess around with a demo project in github.
Homoiconicity is a feature of a language where the language’s “program code is represented as the language’s fundamental data type” (also see Homoiconic Languages on c2).
Javascript is a loosely typed language whose fundamental datatypes are primitives (strings, floats, etc.) and functions (lambdas) with properties (meaning functions can also be used as objects). From these fundamental data-types, we will create a single fundamental data type for Javascript (called a message).
Homoiconic source-code is a data-structure in and of itself. Programming becomes the composition of this single fundamental datatype which results in the new algorithm (the program) and data-structure at the same time.
Programming is the automation of process. We take real world processes and define them in algorithms that operate against data structures.
Data-structures and algorithms are cool. All the software we see today, and other abstractions like objects and functions, are built from these two abstractions: data-structures and algorithms.
The cool thing about data-structures is the things you can do with them. You can:
and a lot of other cool things.
Imagine then a program whose source-code is a data-structure in and of itself. There is a one-to-one mapping of your source-code with the allocated instances of objects on the heap during run-time. This means anything you can do with a data-structure, you can do to a program: even during run time.
And this:
You can display and edit your program using different visual representations!
Just like you can display and edit trees in many different visual formats, you can display and edit source code visually. You can have multiple visual representations of the same program: each one tailored towards the reader of your program!
It really surprises me that we have gone this far without Homoiconicity going main stream.
I think I know why it is hard to find languages that are really Homoiconic. However, I have a really hard time articulating the reasons why. Perhaps it could be articulated as follows:
Homoiconic languages don’t need parameterized sub-routines.
If you are creating a language, and remove parameterized sub-routines as the core abstraction for communication of data between sub-routines, you end up with a Homoiconic language.
The fundamental datatypes (messages) take the place of parameterized sub-routines and passing of information between messages becomes an inherent part of the data-structure you create when programming.
Here is Javascript code that is also a data-structure (both the code and the instances on the heap are a data-structure):
// Write the addition of ((3 + -1) + -1) to the console.
var msgWrite = writeToCon ({
text: add({
left: add({
left: num(3),
right: num(-1)
}),
right: num(-1)
})
});
msgWrite.go; // invoke the program
How about we persist it:
var persistIt = persist ({
fileName: "/somefile",
fileType: "json",
program: msgWrite
});
persistIt.go; // invoke the program
How about we persist it using a file name and file type entered by a user on a form:
var persistIt2 = persist ({
fileName: formFieldGet ({
formName: "persist_form",
formField: "file_name"
}),
fileType: formFieldGet ({
formName: "persist_form",
formField: "file_type"
}),
program: msgWrite
});
persistIt2.go; // invoke the program
How about we inject behavior to write a message to the console when the file_type form field is accessed:
var persistIt3 = persist ({
fileName: formFieldGet ({
formName: "persist_form",
formField: "file_name"
}),
fileType: writeToCon ({
text: formFieldGet ({
formName: "persist_form",
formField: "file_type"
}),
}),
program: msgWrite
});
persistIt3.go; // invoke the program
That’s kinda cool. When the persister-message runs go on the writeToCon-message placed in the fileType property, writeToCon runs the message in the text property.
That eventually causes the text in the form field to propagate up to the writeToCon-message which then writes that text to the console.
The writeToCon-message then propagates the text up to the persist-message which uses the text to determine the file type.
If you look back, you will notice that no matter how complex the program, the interface to it is the exact same. To invoke the behavior of any program you simply call go on that program.
Standardization of behavioral interface is an emergent property of any Homoiconic language and may be a good litmus test to determine if a language is Homoiconic.
Exactly!
In a Homoiconic language, there is no way to distinguish the language itself from the software frameworks built in the language because everything is a fundamental datatype (a message): even scope (see Difficult to Read below).
In my opinion, separation of language and framework is a red-flag that we are doing something wrong.
Fundamental parts of the language are key words (loop, between, forEach, withEach, doWhile, etc.) but the structure of the code does not give any hint as to what those key words are.
var msgLoop = between {
from: num(2),
to: num(8),
by: num(2)
do: writeToCon({
text: str("Hello world!")
})
}
msgLoop.go; // invoke the program
Traditional code would give us a hint as to what the keywords are:
for (int i=2; i<=8; i++) {
Console.WriteLn ("Hello world!")
}
As pointed out by this great blog post on the subject by blue, “it is hard for humans to visually parse as the uniformity of the language often removes any visual cues that we are familiar with in most of the languages”.
You can see an example of a web server configured from scratch here using a Homoiconic language called SipCoffee. In 50 lines of code, we compose a multi-threaded web server that supports socket routing. But it is a little difficult to read at first due to a lack of visual cues. However, remember that since the code is a data-structure, we can display the code in anyway we like: even a visual representation of the code (hint hint).
Perhaps, some day, we will end up with visual programming languages that, behind the scenes, are simply manipulating code that is a data-structure.
Although there is still debate as to what makes a language Homoiconic, I think we have shown that it is indeed possible to program in Javascript using only a fundamental datatype (a message).
Our programs are indeed data-structures both in code and when instantiated on the heap.
Our code being a data-structure gives us the ability to manipulate code just like any data-structure. This means we can join, cut, copy, merge, delete, search, sort, load and save code: even during runtime!
If you find our work on Homoiconicity interesting, please follow us @interfaceVision and/or @erichosick.