3 ms·
> why the theoretical purity of the lisps isn't useful in practice. Saying it's useless may be too strong. People use it. You and I are using it right now to c
by very_mediocre 9y ago
> why the theoretical purity of the lisps isn't useful in practice.
Saying it's useless may be too strong. People use it. You and I are using it right now to comment on HN.
> But lisp macros don't seem to have an example of why they are worth that trade.
This may be the core of your argument. Ok. Who has some examples to share?
- flavio81 9y agoLet me give you a simple example of the usefulness of macros. Consider _any_ boilerplate code that gets repeated on your application. You know how boilerplate code is not only tedious to write, but also creates a maintenance headache whenever you need to modify 'the boilerplate' and thus go copy-paste again through a lot of the code. Macros totally destroy boilerplate. They reduce boilerplate to exactly zero. The other usefulness is that, since they manipulate source code, they are more powerful than functions. That is, the macro can be seen as defining an ultra-powerful version of the function. This enables you to create functions (actually, macros) that do more than functions in other languages. Here is a simple example from Conrad Barski, who has a tutorial where one creates a text-only adventure. This code defines a specific action the user can perform. In this case, to "weld" a "chain" to a "bucket". (defparameter *bucket-filled* nil) (defun weld (subject object) (cond ((and (eq *location* 'attic) (eq subject 'chain) (eq object 'bucket) (have 'chain) (have 'bucket) (not *chain-welded*)) (setf *chain-welded* 't) '(the chain is now securely welded to the bucket.)) (t '(you cannot weld like that.)))) The code defines action "weld" for a subject and an object, but just specifically, for the subject that is "chain" and the object that is "bucket", at the location "attic". As you may infer, each time a new "action" needs to be defined (for a subject, object, in a specific location), a new function will have to be defined and this function will look pretty similar to the above. So after four or five actions you define, you will have lots of unnecesary boilerplate code. Enter macros. The programmer understands there is a "pattern" there, and creates a macro "game-action": (defmacro game-action (&key command subj obj place code) `(defmacro ,command (subject object) `(cond ((and (eq *location* ',',place) (eq ',subject ',',subj) (eq ',object ',',obj) (have ',',subj)) ,@',code) ;; expand the rest of the code (t '(i cant ,',command like that.))))) What this macro does is abstract this repetitive boilerplate and formalizes it. So now one can define the same "weld" game action like this: (game-action :command weld :subj chain :obj bucket :place attic :code (cond ((and (have 'bucket) (setf *chain-welded* t)) '("the chain is now securely welded to the bucket.")) (t '("you do not have a bucket.")))) As you can see, now: a. the definition of a game-action is much more easier to understand, b. boilerplate is reduced (it can be reduced even further in fact) c. and thus you can define more game actions in a more brief way. d. The code that verifies if the subject and object do exist, and if the place is correct, is abstracted away, so you can switch to different data structures in the future, yet your game-action definitions will need no change. Unlike the 'boilerplate' version which will need manual edition of all the game-action definitions. You can apply macros over macros, so there is no limit on the power of them.