Any decent Lisp editor will do automatic indenting, so you can still look at tab depth for meaning.
You could always try Clojure, which swaps out () for [] and {} for various things (function arguments, bindings, array literals, map/set literals, etc.)
At first look, Lisp is ugly to look at.
However once you start learning Lisp and writing your self programs, it becomes fairly easy to read not only your code but others' code.
> The parentheses and indentations obscure the logical meaning of the code.
Indentations have no effect in Lisp, they are only there to make code more readable.
Parentheses are there to clearly tell you which statement is acting over which other statement, so they are there to give you a clear light about the meaning of the code.
Visual appeal can be just fine, as long as the indentation is well-applied. The same is true for widely used languages like Java, C, C++, C#, and Javascript. Wrongly indented C code can be frustrating to read.
Have this small, silly snippet of Common Lisp code and take a look. You should understand easily what each statement is doing.
(defclass my-stack ()
((name
:initarg :name
:initform "<Unnamed stack name>"
)
(the-stack
:initform ()
)))
So here we DEFine a class called "my-stack". With two members (or "slots") called "name" and "the-stack". "Name" will be an initial optional argument, with default value ("initial form") "<Unnamed stack name>" (defmethod into (item (s my-stack))
"Push an item into our stack"
(with-slots (the-stack) s
(push item the-stack)))
Here we DEFine a silly method which pushes an item into an object S of type "my-stack". The explanation "Push an item into our stack" follows.
Later what we say is, that, using the slot "the-stack", perform the following code: Push an item into the stack. Those two lines are almost English. (defmethod sum-all ((s my-stack))
"Return the sum of all items in our stack"
(with-slots (the-stack) s
(loop for item in the-stack
summing item)))
Here I show you that code can be really straightforward to read. Here to sum all the stack i use the following loop instruction:"loop for item in the-stack summing item"
... and it does exactly what it seems to be doing. It's just as if it was plain english!
So one can write highly readable Common Lisp code if one wants to.
Maybe for you; Lisp was great for me to look at from the moment I laid eyes on it.
Beauty is very subjective; but the technical advantages or disadvantages of a syntax aren't.
Some years prior to becoming a Lisp programmer, I had one tiny interaction with Lisp that wasn't terribly good. It was this: some CLOS example code in a book on OOP by Grady Booch. I was trying to follow the syntax and it seemed to have weird repetition in it. That was arising from the defclass syntax for declaring accessors and initforms, like this (foo :initarg :foo) and (bar :accessor bar) type stuff. I was boggled by all the repetitions: why do we have foo and :foo, and so on. I had no clue about Lisp keyword symbols at that time.
I didn't hate the syntax, and it seemed less verbose than C++, for sure. The parentheses in it seemed perfectly fine. It was just boggling a little bit: more so than something like (let ((x 2) (y 3)) (+ x y)), or (loop for x below 10 summing x).
For what it's worth, here is my "defclass-easy" macro which will create a class with the slots you want, it also has a "required-slots" list as a parameter. Slots that are in both "slot-list" and "required-slots" will have an ":initarg" of the same name as the slot, and will raise an error if not supplied when making an object.
Enjoy!
(defmacro defclass-easy (class-name slot-list required-slots)
;;required-slots should be a list as well
`(defclass ,class-name ()
,(mapcar (lambda (x)
(if (null(member x required-slots))
(list x)
(list x :initarg
(read-from-string (concatenate 'string ":" (string x))) ;creates :initarg :<slot name>
:initform `(error ,(concatenate 'string "initarg required: " (string x)))))) ; (error "initarg required: xxxx")
slot-list)))P.S.
(read-from-string (concatenate 'string ":" (string x)))
==> (intern (string x) 'keyword)
`(error ,(concatenate 'string "initarg required: " (string x))
==> `(error "initarg required: ~s" ',x)Disagree 100%. That code is incomprehensible to me. And I don't think it's just because I haven't written Lisp in a long time. I can look at code in other languages that I've never used (eg Haskell) and it makes sense.
Part of the problem, of course, is that there are no type annotations. But even other languages without type annotations are easier for me to read.
The class MY-STACK is a type.
(defmethod into (item (s my-stack))
"Push an item into our stack"
(with-slots (the-stack) s
(push item the-stack)))
In above S is of type/class MY-STACK. ITEM is of class T, because there is no limitation defined.You could define a method for items of type NUMBER:
(defmethod into ((item number) (s my-stack))
"Push an item into our stack"
(with-slots (the-stack) s
(push item the-stack)))
> I can look at code in other languages that I've never used (eg Haskell) and it makes sense.That's unlikely. There is zero chance that somebody not knowing Haskell can understand any non-trivial Haskell code. Haskell has so many different concepts and a different notation including type inference and complex types, that it is difficult to understand without prior Haskell knowledge.
LISP without parentheses.