Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a language have Lisp's powerful macros without the parentheses?

Can a language have Lisp's powerful macros without the parentheses?

like image 609
andi Avatar asked Apr 27 '10 11:04

andi


People also ask

Why does Lisp use parentheses?

They mean that there needs to be a simple way of representing a tree structure within Lisp code. The purpose of the parentheses is to enable that representation. In particular, an open parenthesis, "(", means "step down a level of the tree". And a close parenthesis, ")", means "step back up a level of the tree".

What is so special about Lisp macros?

The special power that Lisp macros have is that they can control evaluation (as seen by evaluating the input expression via ~expr and do arbitrary source-to-source transformations with the full power of the language available.

How can you define macros in Lisp give an example?

Syntax for defining a macro is − (defmacro macro-name (parameter-list)) "Optional documentation string." body-form. The macro definition consists of the name of the macro, a parameter list, an optional documentation string, and a body of Lisp expressions that defines the job to be performed by the macro.

What is a Lisp macro?

The Common Lisp macro facility allows the user to define arbitrary functions that convert certain Lisp forms into different forms before evaluating or compiling them. This is done at the expression level, not at the character-string level as in most other languages.


2 Answers

Sure, the question is whether the macro is convenient to use and how powerful they are.

Let's first look how Lisp is slightly different.

Lisp syntax is based on data, not text

Lisp has a two-stage syntax.

A) first there is the data syntax for s-expressions

examples:

(mary called tim to tell him the price of the book)  (sin ( x ) + cos ( x )) 

s-expressions are atoms, lists of atoms or lists.

B) second there is the Lisp language syntax on top of s-expressions. Not every s-expression is a valid Lisp program.

(3 + 4) 

is not a valid Lisp program, because Lisp uses prefix notation.

(+ 3 4) 

is a valid Lisp program. The first element is a function - here the function +.

S-expressions are data

The interesting part is now that s-expressions can be read and then Lisp uses the normal data structures (numbers, symbols, lists, strings) to represent them.

Most other programming languages don't have a primitive representation for internalized source - other than strings.

Note that s-expressions here are not representing an AST (Abstract Syntax Tree). It's more like a hierarchical token tree coming out of a lexer phase. A lexer identifies the lexical elements.

The internalized source code now makes it easy to calculate with code, because the usual functions to manipulate lists can be applied.

Simple code manipulation with list functions

Let's look at the invalid Lisp code:

(3 + 4) 

The program

(defun convert (code)    (list (second code) (first code) (third code)))  (convert '(3 + 4)) -> (+ 3 4) 

has converted an infix expression into the valid Lisp prefix expression. We can evaluate it then.

(eval (convert '(3 + 4))) -> 7 

EVAL evaluates the converted source code. eval takes as input an s-expression, here a list (+ 3 4).

How to calculate with code?

Programming languages now have at least three choices to make source calculations possible:

  1. base the source code transformations on string transformations

  2. use a similar primitive data structure like Lisp. A more complex variant of this is a syntax based on XML. One could then transform XML expressions. There are other possible external formats combined with internalized data.

  3. use a real syntax description format and represent the source code internalized as a syntax tree using data structures that represent syntactic categories. -> use an AST.

For all these approaches you will find programming languages. Lisp is more or less in camp 2. The consequence: it is theoretically not really satisfying and makes it impossible to statically parse source code (if the code transformations are based on arbitrary Lisp functions). The Lisp community struggles with this for decades (see for example the myriad of approaches that the Scheme community has tried). Fortunately it is relatively easy to use, compared to some of the alternatives and quite powerful. Variant 1 is less elegant. Variant 3 leads to a lot complexity in simple AND complex transformations. It usually also means that the expression was already parsed with respect to a specific language grammar.

Another problem is HOW to transform the code. One approach would be based on transformation rules (like in some Scheme macro variants). Another approach would be a special transformation language (like a template language which can do arbitrary computations). The Lisp approach is to use Lisp itself. That makes it possible to write arbitrary transformations using the full Lisp language. In Lisp there is not a separate parsing stage, but at any time expressions can be read, transformed and evaluated - because these functions are available to the user.

Lisp is kind of a local maximum of simplicity for code transformations.

Other frontend syntax

Also note that the function read reads s-expressions to internal data. In Lisp one could either use a different reader for a different external syntax or reuse the Lisp built-in reader and reprogram it using the read macro mechanism - this mechanism makes it possible to extend or change the s-expression syntax. There are examples for both approaches to provide a different external syntax in Lisp.

For example there are Lisp variants which have a more conventional syntax, where code gets parsed into s-expressions.

Why is the s-expression-based syntax popular among Lisp programmers?

The current Lisp syntax is popular among Lisp programmers for two reasons:

1) the data is code is data idea makes it easy to write all kinds of code transformations based on the internalized data. There is also a relatively direct way from reading code, over manipulating code to printing code. The usual development tools can be used.

2) the text editor can be programmed in a straight forward way to manipulate s-expressions. That makes basic code and data transformations in the editor relatively easy.

Originally Lisp was thought to have a different, more conventional syntax. There were several attempts later to switch to other syntax variants - but for some reasons it either failed or spawned different languages.

like image 78
Rainer Joswig Avatar answered Oct 15 '22 10:10

Rainer Joswig


Absolutely. It's just a couple orders of magnitude more complex, if you have to deal with a complex grammar. As Peter Norvig noted:

Python does have access to the abstract syntax tree of programs, but this is not for the faint of heart. On the plus side, the modules are easy to understand, and with five minutes and five lines of code I was able to get this:

>>> parse("2 + 2")

['eval_input', ['testlist', ['test', ['and_test', ['not_test', ['comparison', ['expr', ['xor_expr', ['and_expr', ['shift_expr', ['arith_expr', ['term', ['factor', ['power', ['atom', [2, '2']]]]], [14, '+'], ['term', ['factor', ['power', ['atom', [2, '2']]]]]]]]]]]]]]], [4, ''], [0, '']]

This was rather a disapointment to me. The Lisp parse of the equivalent expression is (+ 2 2). It seems that only a real expert would want to manipulate Python parse trees, whereas Lisp parse trees are simple for anyone to use. It is still possible to create something similar to macros in Python by concatenating strings, but it is not integrated with the rest of the language, and so in practice is not done.

Since I'm not a super-genius (or even a Peter Norvig), I'll stick with (+ 2 2).

like image 40
Ken Avatar answered Oct 15 '22 08:10

Ken