The number one feature that every Lisper brags about is that Lisp is homoiconic, which is just a fancy word for Lisp code is Lisp data. Code written in Lisp is just a bunch of nested lists, for example:
(+ 1 2 3 4 (* 2 3))
Over here we just have a list of 6 elements. The Lisp reader can easily read that form and evaluate it, which produce the value 16. You might be wondering "Ok?, so code is data, what's the big deal?". Well, the big deal is that powerful macros are now possible. A lot of people think of C-like macros when they hear the word macros, but Lisp Macros are much more elegant and powerful. C-like macros are basically text substitution facilities. Lisp macros are special Lisp functions that don't evaluate their arguments. Basically, macros receive a data structure that is not evaluated, manipulate it, and then return a data structure that is meant to be evaluated. So that means you now have the power of syntactic abstraction. You can extend the language and define new constructs. As a very simple example, I can make a for loop macro in common lisp like this
(defmacro for (var start stop &rest body)
(let ((gstop (gensym)))
`(do ((,var ,start (1+ ,var))
(,gstop ,stop))
((> ,var ,gstop))
,@body)))
and I can use it like this
;;prints numbers from 0 to 10
(for i 0 10
(format t "~D~%" i))
Another example that I found pretty cool, was adding currying to Clojure.
(defmacro defcurried [fname bindings & body]
(let [n# (count bindings)]
`(def ~fname (fn [& args#]
(if (< (count args#) ~n#)
(apply partial ~fname args#)
(let [fn# (fn ~bindings ~@body)]
(apply fn# args#)))))))
I can now define a curried function as
(defcurried add [a b c d]
(+ a b c d))
and in the repl
(add 1) => #<core$partial$fn__4228 clojure.core$partial$fn__4228@39cc269e>
((add 1) 5) => #<core$partial$fn__4230 clojure.core$partial$fn__4230@588f258b>
(((add 1) 5) 3 4) => 13
(add 1 5 3 4) => 13
(((add 1 5) 3) 4) => 13
;;etc
It doesn't stop there, you can extend the language as you please. For example, when OOP was first introduced in common lisp, it was implemented as a bunch of macros and functions, no changes to the compiler were made, how cool is that?.
One cool thing about making your own constructs in Lisp, is that it's easy, you don't need any special tools or an API to the compiler, and the resulting construct doesn't look foreign to the language. Whatever macros you make would look like something that was built-into the language. Most other languages don't give you that power, if you need a feature or construct added, you have to request it from the language maintainer, and it either gets rejected or accepted. If it does get accepted, you have to wait for the next release of the language to be able to use it. In Lisp, you don't have to wait or be under the mercy of anyone, you can implement whatever abstractions you need by yourself and use them instantly. In other languages, the only facilities at your disposal for building abstractions are usually functions/methods and OOP features. You can do a lot with them, but they will always look foreign, you will never get the native look and feel that Lisp provides, plus OOP usually gets really bulky with lots of class hierarchies. Also, macros give you the ability to selectively choose what gets evaluated and what doesn't, so you have full control over the semantics of the abstraction you're making. With Lisp macros, people can build entire languages that target a specific problem (DSLs), so you have the power to tailor the language the way you like so you can express the problem you're trying to solve more elegantly. Some people would say that you don't really need macros and it's all just showing off, but that's what other languages have taught you. Paul Graham (this guy REALLY loves Lisp) once said in one of his Lisp books that programming languages teach you to not want what they can't provide, and I agree with him. If you've never heard or used macros before, you don't really know what you're missing, and it's natural that you won't really want to use them. However, using macros is common practice in Lisp, everyone uses them, and everyone loves them.
If you want to learn more about Lisp, then I highly recommend
ANSI Common LISP: Paul Graham: 9780133708752: Amazon.com: Books
On Lisp
Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp: Peter Norvig: 9781558601918: Amazon.com: Books
Structure and Interpretation of Computer Programs
These books will help you "get" it.
(+ 1 2 3 4 (* 2 3))
Over here we just have a list of 6 elements. The Lisp reader can easily read that form and evaluate it, which produce the value 16. You might be wondering "Ok?, so code is data, what's the big deal?". Well, the big deal is that powerful macros are now possible. A lot of people think of C-like macros when they hear the word macros, but Lisp Macros are much more elegant and powerful. C-like macros are basically text substitution facilities. Lisp macros are special Lisp functions that don't evaluate their arguments. Basically, macros receive a data structure that is not evaluated, manipulate it, and then return a data structure that is meant to be evaluated. So that means you now have the power of syntactic abstraction. You can extend the language and define new constructs. As a very simple example, I can make a for loop macro in common lisp like this
(defmacro for (var start stop &rest body)
(let ((gstop (gensym)))
`(do ((,var ,start (1+ ,var))
(,gstop ,stop))
((> ,var ,gstop))
,@body)))
and I can use it like this
;;prints numbers from 0 to 10
(for i 0 10
(format t "~D~%" i))
Another example that I found pretty cool, was adding currying to Clojure.
(defmacro defcurried [fname bindings & body]
(let [n# (count bindings)]
`(def ~fname (fn [& args#]
(if (< (count args#) ~n#)
(apply partial ~fname args#)
(let [fn# (fn ~bindings ~@body)]
(apply fn# args#)))))))
I can now define a curried function as
(defcurried add [a b c d]
(+ a b c d))
and in the repl
(add 1) => #<core$partial$fn__4228 clojure.core$partial$fn__4228@39cc269e>
((add 1) 5) => #<core$partial$fn__4230 clojure.core$partial$fn__4230@588f258b>
(((add 1) 5) 3 4) => 13
(add 1 5 3 4) => 13
(((add 1 5) 3) 4) => 13
;;etc
It doesn't stop there, you can extend the language as you please. For example, when OOP was first introduced in common lisp, it was implemented as a bunch of macros and functions, no changes to the compiler were made, how cool is that?.
One cool thing about making your own constructs in Lisp, is that it's easy, you don't need any special tools or an API to the compiler, and the resulting construct doesn't look foreign to the language. Whatever macros you make would look like something that was built-into the language. Most other languages don't give you that power, if you need a feature or construct added, you have to request it from the language maintainer, and it either gets rejected or accepted. If it does get accepted, you have to wait for the next release of the language to be able to use it. In Lisp, you don't have to wait or be under the mercy of anyone, you can implement whatever abstractions you need by yourself and use them instantly. In other languages, the only facilities at your disposal for building abstractions are usually functions/methods and OOP features. You can do a lot with them, but they will always look foreign, you will never get the native look and feel that Lisp provides, plus OOP usually gets really bulky with lots of class hierarchies. Also, macros give you the ability to selectively choose what gets evaluated and what doesn't, so you have full control over the semantics of the abstraction you're making. With Lisp macros, people can build entire languages that target a specific problem (DSLs), so you have the power to tailor the language the way you like so you can express the problem you're trying to solve more elegantly. Some people would say that you don't really need macros and it's all just showing off, but that's what other languages have taught you. Paul Graham (this guy REALLY loves Lisp) once said in one of his Lisp books that programming languages teach you to not want what they can't provide, and I agree with him. If you've never heard or used macros before, you don't really know what you're missing, and it's natural that you won't really want to use them. However, using macros is common practice in Lisp, everyone uses them, and everyone loves them.
If you want to learn more about Lisp, then I highly recommend
ANSI Common LISP: Paul Graham: 9780133708752: Amazon.com: Books
On Lisp
Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp: Peter Norvig: 9781558601918: Amazon.com: Books
Structure and Interpretation of Computer Programs
These books will help you "get" it.
Comments
Post a Comment