Many years ago I was under the delusion that if Lisp were more “normal looking” it would be adopted more readily. I thought that maybe inferring the block structure from the indentation (the “off-sides rule”) would make Lisp easier to read. It does, sort of. It seems to make smaller functions easier to read, but it seems to make it harder to read large functions — it's too easy to forget how far you are indented if there is a lot of vertical distance.
I was feeling pretty good about this idea until I tried to write a macro. A macro’s implementation function has block structure, but so does the macro’s replacement text. It becomes ambiguous whether the indentation is indicating block boundaries in the macro body or in it’s expansion.
A decent macro needs a templating system. Lisp has backquote (aka quasiquote). But notice that unquoting comes in both a splicing and non-splicing form. A macro that used the off-sides rule would need templating that also had indenting and non-indenting unquoting forms. Trying to figure out the right combination of unquoting would be a nightmare.
The off-sides rule doesn’t work for macros that have non-standard
indentation. Consider if you wanted to write a macro similar
to unwind-protect
or try…finally
.
Or if you want to have a macro that expands into just
the finally
clause.
It became clear to me that there were going to be no simple rules. It would be hard to design, hard to understand, and hard to use. Even if you find parenthesis annoying, they are relatively simple to understand and simple to use, even in complicated situations. This isn’t to say that you couldn’t cobble together a macro system that used the off-sides rule, it would just be much more complicated and klunkier than Lisp’s.
6 comments:
What about Tree Notation:
https://treenotation.org/
Another solution (related to Tree Notation) is to take inspiration from Forth. Forth requires no parentheses and ignores indentation, but creation of new syntax is still simple. Of course, Forth can do this because it's more dynamic than even lisp, so a type system would be needed for lisp to allow inference of parentheses at compile-time.
;; Operators
declare - (I &rest 1 I)
declare * (I &rest 1 I)
declare / (I &rest 1 I)
declare ^ (I &rest 1 I)
declare ± (I &rest 1 I)
declare sqrt (I)
declare print (I)
;; Variables
declare a L
declare b L
declare c L
;; Quadratic equation
print / ± (- b)
sqrt - ^ b 2
(* 4 a c)
* 2 a
Heresy time. I like C syntax. Let's make curly braces expand to `progn`.
defun double (v) {
* 2 v
}
expands to
(progn
(declare double (I))
(defun double (v)
(progn
(* 2 v))))
The disadvantage of this syntax is that whitespace is not taken into account at all, and I know some people do like Python's syntax.
Oh nice… The post ate my code indentation. Well, fortunately if an interpreter is ever written for this the code will still execute since it ignores whitespace. :)
The way that comments are treated are a good argument against significant whitespace.
Test: no indent
Test: 4-NBSP indent
What sort of syntax for the macro call were you trying to use? Macros seem to work fine with most significant whitespace systems I've seen.
Maybe the best solution for off-sides rule is to compromise on macro calls?
defmacro until (condition &rest body)
`while (not ,condition)
,@body ; Parentheses are inferred, then each form is inserted at the current indentation level.
let ((x 0)
(y 1))
until {x = 10}
print y
incf x
setf y {2 * y}
If you don't like the parentheses used in `let`, then Sweet Expressions proposes this syntax (which i think is ugly):
let
group
x 0
y 1
until {x = 10}
print y
incf x
setf y {2 * y}
`group` would be a macro.
Post a Comment