Is homoconicity necessary for macros?

2012-02-04 is sponsored by the word "homoiconicity" http://en.wikipedia.org/wiki/Homoiconicity .

Background: I’m going to choose which book about Clojure to buy - “Clojure in action” or (coming in late April) “Clojure Programming” (you can read it through O'Reilly Rough Cuts , half the pages are visible), I was struck that in both books this property - homoconicity - caused a great emphasis.

Since Clojure roots are based on Lisp, I called the "dead sexy" book - it really is that important. Well, I see that the macros are explained in detail, but I did not notice the accent mentioned. Now compare with this (quote from "Clojure in action")

This homoconicity also enables the Clojure s macro system.

It seems that macros would not be possible without it. Even Wikipedia's statement (link above) is more balanced, but none of these sources (*) take human factors into account in favor of English syntax.

If I'm not mistaken, a syntax macro (Lisp -like) may be possible in C # (for example), only with great effort from the C # team. But this is the value of the design team, not the users (?). The second is the question. If in real life you think “a + b”, and in the computer world you constantly translate it to “+ ab”, performance suffers (I see this for myself when I switched from C ++ functors to C # lambdas).

This praise that the Clojure programmer writes programs almost directly, like AST, scares me by reading that "by writing code directly in hexadecimal code, you not only learn the six system by heart, but also closer to the machine."

To summarize - I like metaprogramming, I like Lisp macros (I'm not Lisper, though), but I see two things here - macros and homoconicity. The first is, without a doubt, a big one, the second is not so much (as for my understanding), because it requires a person's need for a computer, and it has to be in a different way.

Question

Is homoiconicity really beneficial for people (end users of the language), or is it almost exclusively useful for language developers? Examples are very welcome!

Or just in case, if I rephrase - if the given language has Lisp -macros, will the "homocinity" add end-user productivity? Expresiveness? Or quite the opposite?

(*) I can’t be 100% sure, because I see only a small part of Clojure books, and I don’t read them, just evaluating them for purchase.

Update

Thanks to everyone for the answers, it’s a pity that I needed to choose only one solution :-), this does not mean that I value others less, this one is the most complete for me.

+8
macros design lisp clojure
source share
5 answers

Homoconicity is not strictly required for macros (example: the C / C ++ preprocessor implements a compile-time macro).

However, homoiconcity makes macros more efficient and easier to use:

  • You avoid parsing: in a homicomicon language like Clojure, the source code can be directly used as an abstract compiler syntax tree. Less overhead for the compiler, less clear overhead for the user.
  • Code generation is simpler - you just need to put together the correct data structure, and not carefully create text source code in a form that can be compiled. In general, generating text code can be quite a difficult problem in languages ​​that were not designed for it (you need to consider a lot of things, such as creating symbol names, importing libraries, quoting rules, does the compiler need a source file to write a disc, etc.) d.)
  • The macro language is the same as your original language. No need to learn a single language or set of constructs for macros.
  • homoiconicity IMHO makes macros simpler, more elegant, and more readable. In many cases, your macro looks just like a template for the right code.

As a small example, here is a macro that adds a Java / C # style for the loop to Clojure.

(defmacro for-loop [[sym init check change :as params] & steps] `(loop [~sym ~init value# nil] (if ~check (let [new-value# (do ~@steps)] (recur ~change new-value#)) value#))) ;; Usage: (for-loop [i 0 (< i 10) (inc i)] (println i)) 

And that this is a new language construct added in six lines of code. You can clearly see homoconicity in action — the entire “loop” form is just a quoted Clojure data structure. The init, check, and change parameters are also homoiconic Clojure data structures passed to the macro, which are loop control expressions. There was no need to parse the syntax to support my new syntax.

I think it would be very difficult to do this as concisely or simply in any non-homiconic language.

+9
source share

No, you don’t need homoconicity to create a powerful macro system like Lisp. Homoconicality simply makes such a system much easier to use; without it, you will need a different syntax for expressing the ASTs that the macros produce during evaluation. In a homocyconic language, a macro-system feels natural, since programs and metaprograms look the same.

+10
source share

Homoconism is a poorly defined concept.

What sets Lisp apart is that it has a representation of the source code data, and the evaluator accepts it as input. Processing source code as data is easy because all the usual Lisp functions are used.

What it provides:

  • reading a text source and creating its data structures
  • processing source as data
  • printing "source as data" as text, including things like beautiful code printing

Macros are functions that transform source code. Thus, all you need is some representation of the source code and a step in the execution / compilation / interpretation of the programming language, where this conversion step takes place.

The way Lisp works makes it convenient, since the source code is a data structure that can be manipulated with many built-in functions and tools. Please note that more complex conversions, when you need to understand the syntax of the Lisp language, are also not easy to do in Lisp - although the tools were implemented (for example, the so-called code walkers).

There are also Lisps that have syntax that is not based on s-expressions. An example is RLISP, the implementation language of the computer algebra system REDUCE. RLISP also supports macros.

You can define a similar pattern based on strings and string manipulations. You can also define a macro system based on some kind of AST.

An example of languages ​​with such macros:

+5
source share

Imagine using the langage methodology of old and inconvenient web templates using <% code %> to use inline java in python.

Then the macro language will be java and the target language will be python. Calling a macro would mean exiting the <% code %> mode. You can even create such a system to support parameters, evaluate strings, etc. That would be really ugly (like old jsp or php code).

A terrible thought: could some jsp users find such a system more comfortable than the lisp system? Will this monster be improved if the macro / target language is the same?

So, of course, this can be done. People have done some amazing things with C ++ templates. Jay Freeman wrote a C ++ program with Ackermann compilation times and constant runtimes to demonstrate that C ++ templates could do such a thing (apparently because the Fibonacci examples did not explode enough compilers?).

As for homoconicity: it does not solve everything. Even with him often there is some kind of awkwardness: quotes, quasi-quotation marks, etc. You need some way to switch between "this is the macro code" and "this is the target language code." This is an inherent problem faced by language developers. Instead of a lispy language with lists in the form of data structures or program code, you could only think about the annoyance of using strings with the eval method. The program source is like a string. A string is a really terrible data structure. And the lines in the chains are even worse. Look at any code in your code (maybe not lisp). They can be imagined as homoconic, done terribly wrong.

+3
source share

While you need a language for macros, it doesn't have to be the same language the rest of your code is written in. all you need is the "iconicity" *, the "homo" part is optional.

In Ocaml, macros are written in another language called camlp4

it looks something like this :

 #load "pa_extend.cmo"; value gram = Grammar.gcreate (Plexer.gmake ()); value exp = Grammar.Entry.create gram "expression"; EXTEND exp: [ "SUM" [ x = exp; "+"; y = exp -> Op "+" xy | x = exp; "-"; y = exp -> Op "-" xy] | "PROD" [ x = exp; "*"; y = exp -> Op "*" xy | x = exp; "/"; y = exp -> Op "/" xy] | "ATOM" [ x = INT -> Int (int_of_string x) | x = LIDENT -> Var x | "("; x = exp; ")" -> x ] ] ; END; 

where the language it converts looks like this:

 let rec search ?(x=0) ?(y=0) f accu = match x, y with 9, y -> search ~x:0 ~y:(y+1) f accu (* Next row *) | 0, 9 -> f accu (* Found a solution *) | x, y -> if m.(y).[x] <> '0' then search ~x:(x+1) ~yf accu else fold (fun accu n -> let n = Char.chr (n + 48) in if invalid xyn then accu else (m.(y).[x] <- n; let accu = search ~x:(x+1) ~yf accu in m.(y).[x] <- '0'; accu)) accu 1 10 

since you can see that in Ocaml there is no homocyticity, and it has a highly developed macro-system (for example).

  • This is not a real term, I did it in the hope that someone would smile :)
+2
source share

All Articles