Hacker News new | ask | show | jobs
by mattfenwick 4285 days ago
This is my understanding of homoiconicity. There's a good chance that I'm wrong. Comments and clarifications welcome!

-----

Is homoiconicity about concrete syntax? I think the answer is no , but I also think that this confuses lots of people because it's not explicitly stated.

Then is homoiconicity about abstract syntax? Let's say we have some arbitrary language, for which there's an eval function (input: abstract syntax tree, output: some data structure) whose job is to execute/evaluate a program in that language. Homoiconicity, then, is the case where the output data structure is also an abstract syntax tree, or conversely, that an abstract syntax tree is expressed in data structures of the language.

So for homoiconicity, the input and output of your eval function are of the same type (of course the type may be a complicated algebraic sum type etc.). Thus the input and output have the same domain. This means that you can take the output from eval, and throw it back into eval again without getting a type error.

If the input and output types of the eval function don't match, then the language isn't homoiconic.

Is this correct?

What I don't get is whether the eval function is available from inside or outside the language, or both.

I'm also unsure of whether homoiconicity is a property of a language, or of an implementation of a language.

2 comments

I'no authority on this but I think you are right and gave the best explanation, even definition of homoiconity I have read so far: You can take the output of eval and pass it back as further input to eval. In other words input and output of eval must have the same data-type.

A good example is JavaScript, often called a "lisp-like" language. In JavaScript you have the function Function() which takes a String as input and produces a Function. Thus a JavaScript program can create another program which it executes within itself. But it is not homoiconic: input of Function() is a String, and its output is a Function. You can not pass the output of Function() as an input to another call of Function().

The most coherent definition I can come up with is that homoiconicity describes the relationship between language syntax and a data representation, such that all valid programs can be parsed by a conforming data parser, and the parser output contains all the information required to compile or execute the program.

Thus, all programs are homoiconic to some data format (if only flat text files). The interesting thing about Lisp is that it's homoiconic to s-expressions, which it also provides powerful tools to manipulate. This paves the way for its macro system which provides compile-time support for processing s-expressions and treating the output as input to the compiler.

Your definition (already rather loose) covers only one direction. The point of homoiconicity is that code and data are the same, so they must be easily interchangeable both ways, and also during runtime.

Even if you would consider flat text as an acceptable data format for homoiconicity, most languages don't fit that criteria. Can a random program in a random language (not expressly written as a quine) easily obtain itself in this representation? Can I easily write a function that, when passed any arbitrary function as an argument, obtain the flat text representation of that function, modify it, and execute that modified function?

Does this even work properly for Lisps with lexical scoping? I'm not that familiar...

Modulo scoping, you can do that in JavaScript.

I think the word syntax is ambiguous. Is homoiconicity about concrete syntax? Abstract syntax? Both? Some other kind of syntax? Does parsing have something to do with homoiconicity?

I don't know the answers to these questions.