Previous discussions of HomoiconicLanguages got bogged down in definitions. To cut this GordianKnot, let's define a concrete task that is easily implemented in a HomoiconicLanguage but impossible or difficult in other ProgrammingLanguage''''''s. For starters, let's develop a spec from the LispLanguage example from HomoiconicLanguages. * Create a "code" data structure (block) for assigning 15 to a variable * Evaluate it and view the variable's contents (15) * Modify that data structure to assign 37 to the variable * Evaluate it and view the variable's contents (37) * Optional: view the code blocks, if the environment allows it. Prohibited: * Escaping to the OperatingSystem to save source files to disk or perform compilation * Accessing language internals that are not portably defined in the language definition ---- '''LispLanguage''' Here's a demonstration of what homoiconicity looks like in Lisp (one or more semicolons designates a comment): (setf b 3) ; setf means assign, so 3 is being assigned to b b ; b by itself asks the interpreter to print its value 3 ; ...printed by interpreter; b's value is now 3 ;;; assign to a a program fragment which assigns 15 to b: (setf a '(setf b 15)) ;;; take a look at what a refers to now: a (SETF B 15) ; printed by interpreter ;;; evaluate the program fragment that a refers to: (eval a) ;;; ...the assignment of 15 to b has just happened: b ; interpreter prints the value of b, 15 ;;; Now traverse the data structure a refers to, finding the value 15: (car (cdr (cdr a))) ; car means left node of tree, cdr means right node 15 ; printed by interpreter ;;; Now that we have verified where the 15 appears in the program fragment, change it: (setf (car (cdr (cdr a))) 37) ; modifies a's program fragment ;;; take a look at what a refers to now: a (SETF B 37) ; printed by interpreter ;;; evaluate the program fragment referred to by a (eval a) ; executes the program fragment displayed above (setf b 37) b ; check the value of b 37 ; ...printed by interpreter. So we took a program fragment (an assignment) written in normal Lisp syntax, assigned it to a variable, and executed the contents of that variable. Indeed it performed the assignment. Then we modified the data structure representing the assignment, using the normal Lisp operators for operating on any kind of data, and evaluated the same variable again, and indeed the new assignment worked just fine. So we see that data and code are completely interchangeable in Lisp, without extra muss or fuss. Lisp is homoiconic. ''I don't agree on this as a good example. See: >> b=3 => 3 >> b => 3 >> a= "b=15" => "b=15" >> a => "b=15" >> eval a => 15 >> b => 15 >> a[-2..-1] => "15" >> a[-2..-1]="37" => "37" >> a => "b=37" >> eval a => 37 >> b => 37 This is not an homoiconic language, yet the only difference you can find is that a string is treated more like an array than as a cons/list. I could have written #my_eval to take a cons cell s input and completely mimic the above lisp code, but there would be no advantage, the code just shows #eval not homoiconicity.'' * You haven't shown whether this anonymous language is, or is not, homoiconic, but your point about eval is well-taken. The original intent was to aid people in understanding homoiconicity, but you're right that a non-homoiconic language with eval accomplishes this example. What example would be even more to the point? * Wait, why is this not a homoiconic language? Code has the same representation as one of the datatypes, viz. string/array, right? * Homoiconicity can be measured by the degree to which program constructs are represented as first-class entities; ''objects'' if you will. In Lisp, the program is written as a series of nested lists, so it makes sense that the program is naturally represented as such. In IoLanguage, programs are expressed as an abstract syntax tree, the nodes of which are accessible directly in any running Io program. Representing a program as ''a string,'' however, represents the lowest form of homoiconicity, because there is nothing at all high-level or first-class about it. Writing '''a="if"''' means nothing -- does 'a' represent a keyword, or the prefix to a set of Ethernet interfaces? You cannot tell without additional context, since it's just a string of two characters. In a language with a high degree of homoiconicity, the answer would be quite patent. ---- '''ToolCommandLanguage''' Here's the LispLanguage example translated into Tcl, in the form of an interactive session. Comments in Tcl are commands starting with #, so need a semicolon before them to start a new command. Lines entered into the interpreter are prefixed with a % prompt: % set b 3 ;# Assign b the value 3 3 ;# The value of b, echoed by the interpreter % puts $b ;# Print out the value of b. 3 % set a {set b 15} ;# Assign to a the string "set b 15" set b 15 ;# The code, echoed by the interpreter % eval $a ;# Run it using eval 15 % puts $b ;# b is now 15 15 % set a [lreplace $a end end 37] ;# Replace the last item in a with 37 set b 37 % if 1 $a ;# Run the new command by passing it as the third argument to if 37 % puts $b ;# b is now 37 37 Note that, in Tcl, strings are the same as code. The string "set b 15" is a string, but also is a list of three items, and also is code which sets b to 15. You can't run $a by writing % $a since that would try to run a command called "set b 37", which presumably isn't defined, but you can pass it to the 'if' command as the run-if-true block of code. ---- '''JoyLanguage''' As a pure FunctionalProgrammingLanguage, Joy doesn't have variables or assignment. Quotations are usually manipulated to pass on to other combinators. For the purposes of the example, we can posit Forth-like variables (b) and assignment (!) so that the example would be: define f == 7 b ! . (* a function *) f b @ . (* execute the function (7) *) define a == [15 b !] . (* a quotation *) a i (* execute the quotation (i combinator) *) b @ . (* 15 *) 37 a rest cons (* modify the quotation: [37 b !] *) i (* execute the modified quotation *) b @ . (* 37 *) ---- '''IoLanguage''' Following is an interactive session in IoLanguage: Io> b:=3 ==> 3 Io> a:=block(b=15) #assign the code that updates b into 15 ( = is translated into updateSlot method call) ==> block(updateSlot("b", 15)) Io> a #executes the block ==> 15 Io> b ==> 15 Io> getSlot("a") code ==> block(updateSlot("b", 15)) Io> msg:=getSlot("a") message ==> Message_0x61b240 protos(Message_0x61b240) Io> msg argAt(1) setName("37") ==> Message_0x61a240 protos(Message_0x61a240) Io> msg code #see the code has changed ==> updateSlot("b", 37) Io> a ==> 37 Io> b #now b is 37 ==> 37 ---- '''ArrLanguage''' An interactive session in R: > b <- 3 > b [1] 3 > a <- quote(b <- 15) > a b <- 15 > as.list(a) #stripping the chrome [[1]] `<-` [[2]] b [[3]] [1] 15 > a[[3]] [1] 15 > a[[3]] <- 37 > a b <- 37 > eval(a) > b [1] 37 ---- Contributors: DougMerritt, IanOsgood, JuneKim ---- '''Discussion:''' Any ideas on making the spec less trivial? Ideas: * Restructure the code block (add or remove a loop) * Write a function that when given a program (precondition: it doesn't contain macros and it is not self modifying), adds instrumentation code to all if statements, and then provide path coverage upon running the program. ** [Much of the discussion on this item moved to HomoiconicLanguageDrawbacks.] Any other prohibitions? See also DefinitionOfHomoiconic and everything else with "Homoiconic" in the title for more rambling discussion. ---- See HomoiconicExampleInJava for some failed attempts and much discussion. (Java fails to evaluate source without writing to the file system.) ---- CategoryInManyProgrammingLanguages