News for hackers. Stuff that matters.
Re:Why Lisp macros are cool, a Perl perspective (2005) by Rerarom on Monday December 16, 2019 @06:33PM (#21808739)
What curious newbies might miss from discussions of macros is that they are a separate language operating at compile time. They are usually advertised as code operating on itself or something like that. They're not. Powerful, yes, but not that cool.
Re:Why Lisp macros are cool, a Perl perspective (2005) by btilly on Monday December 16, 2019 @06:59PM (#21808974)
Your comment is true about most languages, but false about Lisp.

Lisp has no compile time vs run time distinction. It only has lists. What is done with a list depends on what the first thing in the list is determined to be:

1. Function - evaluate the arguments then pass it to the function.

2. Macro - manipulate the list according to the macro and then get another list.

3. Special Form - follow the special rules for the special form. (The list of special forms varies from Lisp to Lisp, and are the elementary building blocks from which the language was built.)

In Lisp, macros are advertised as code operating on itself, because that is exactly what they are. And yes, they are exactly that cool.

Re:Why Lisp macros are cool, a Perl perspective (2005) by weare138 on Monday December 16, 2019 @07:13PM (#21809100)
A cool example of this was NASA's Deep Space 1 probe. The control system was written in Lisp. There was a race condition in the code that wasn't detected during ground testing. NASA software engineers were able to remotely debug and patch the code while it was still running.

ti.arc.nasa.gov/m/pub-archive/176h/0176%20(Havelund).pdf

Re:Why Lisp macros are cool, a Perl perspective (2005) by ragebol on Tuesday December 17, 2019 @03:24AM (#21811706)
Imagine running a REPL with 45 minutes of light-speed delay in the loop. I love to tell this anecdote
Re:Why Lisp macros are cool, a Perl perspective (2005) by ImprobableTruth on Monday December 16, 2019 @07:32PM (#21809240)
Macros are fully expanded before the rest of the code is executed. Trying to argue that Lisp doesn't have a distinction between compile time and run time because you usually have the compiler (or at least interpreter) available at runtime strikes me as just playing semantics games.
Re:Why Lisp macros are cool, a Perl perspective (2005) by RHSeeger on Tuesday December 17, 2019 @01:33AM (#21811212)
Do you have a citation for this? I was under the impression it was entirely possible for lisp macros to be expanded at runtime. For example, you could have data that is passed to a function and then used as code (because code and data are the same thing). The system doesn't know it's going to be used as code until it is, so the macros can't be expanded until then.
Re:Why Lisp macros are cool, a Perl perspective (2005) by lispm on Tuesday December 17, 2019 @03:44AM (#21811819)
If Lisp uses an interpreter (which runs Lisp source code), you may see macro expansions at runtime.

For compiled code, there usually will be no macro expansion at runtime. Lisp will not macro expand code in compiled code, since all macros need to be expandable at compile-time.

But: if you explicitly want it, you can generate code at runtime and then you need to call EVAL or COMPILE. Then macro expansions might happen.

Re:Why Lisp macros are cool, a Perl perspective (2005) by mc3 on Monday December 16, 2019 @08:09PM (#21809480)
I think this is why it's non-trivial to build a distributable exe of the program, right? The exe would need a lisp interpreter/compiler bundled with it, because at runtime it might generate some code and run it!
Re:Why Lisp macros are cool, a Perl perspective (2005) by earenndil on Monday December 16, 2019 @08:23PM (#21809559)
Except that it's actually very trivial to make such an exe.
Re:Why Lisp macros are cool, a Perl perspective (2005) by mc3 on Monday December 16, 2019 @09:59PM (#21810106)
From [0]:

"Most Common Lisp implementations support creating native-format executable files. The typical route involves loading all of the program's code into the Lisp environment, dumping the current state of the Lisp process into a disk image, and identifying a start function (ie main).

Since the disk image contains the entire Common Lisp system (compiler, debugger, documentation strings, etc.), the executable will be typically larger than a compiled C program (even if you statically link the C code).

Another form of disk image requires a copy of the particular Lisp implementation to be installed on the recipient's machine. This is no different in principle from a C application requiring platform-specific dynamic libraries to run. This complicates deployment - the disk image is tied to a particular implementation version, and you typically end up having to include the Lisp implementation executable anyway.

Due to platform limitations, some Lisp implementations can't produce native executables (for example, ABCL can only make JAR files). Other implementations are only able to dump disk images that require a separate copy of the implementation executable. An alternative to executables is available on Unix platforms in the form of shell scripting."

If it needs 4 wordy paragraphs and you still don't know how to do it, I am going to scribe that up as non-trivial.

[0] https://www.cliki.net/Creating%20Executables

Re:Why Lisp macros are cool, a Perl perspective (2005) by lispm on Tuesday December 17, 2019 @03:49AM (#21811841)
That's because different implementations provide different ways to generate executables. Some are simple, some are complex.

For SBCL one needs to call a function, which creates the executable image.

http://www.sbcl.org/manual/index.html#Function-sb_002dext-sa...

There are more complex machineries for application generation, though.

Re:Why Lisp macros are cool, a Perl perspective (2005) by earenndil on Tuesday December 17, 2019 @02:25AM (#21811442)
You don't need those 4 paragraphs.

I have this alias in my .zshrc:

    clc() { sbcl --no-userinit --load $1 --eval "(sb-ext:save-lisp-and-die \"$(sansext $1)\" :toplevel 'main :executable t)" }
Sansext is a custom function that strips the file extension from a filename, but you don't need that; you can read output filename from $2, or whatever else.
Re:Why Lisp macros are cool, a Perl perspective (2005) by mc3 on Tuesday December 17, 2019 @10:14PM (#21821434)
Nice. I think it is important for proponents of languages to answer FAQs like this so you aren't left in a corner when you want do so something (i.e build & distribute) that is standard in most languages.
Re:Why Lisp macros are cool, a Perl perspective (2005) by Rerarom on Monday December 16, 2019 @07:02PM (#21809000)
Every reference on Lisp macros says that they are executed at compile-time.
Re:Why Lisp macros are cool, a Perl perspective (2005) by btilly on Monday December 16, 2019 @07:09PM (#21809065)
From http://www.paulgraham.com/icad.html: There is no real distinction between read-time, compile-time, and runtime. You can compile or run code while reading, read or run code while compiling, and read or compile code at runtime.

That is from one of the founders of YCombinator, the author of http://www.paulgraham.com/onlisptext.html and http://www.paulgraham.com/acl.html.

Re:Why Lisp macros are cool, a Perl perspective (2005) by lispm on Monday December 16, 2019 @09:20PM (#21809872)
There is a distinction. I can compile code while reading, but when I'm compiling, then I'm in compile-time.

In a compiled implementation, code needs to be compiled before it gets run. When I want it to be compiled, I have to call functions like COMPILE or COMPILE-FILE.

Re:Why Lisp macros are cool, a Perl perspective (2005) by Rerarom on Monday December 16, 2019 @07:18PM (#21809148)
That's the kind of misleading text I was talking about in my first comment.
Re:Why Lisp macros are cool, a Perl perspective (2005) by ScottBurson on Monday December 16, 2019 @08:00PM (#21809427)
There is a conceptual distinction between read time, compile time, and run time, but it's only conceptual: all these things happen in a single process in an interleaved manner. When you type an expression into the REPL, first it gets read, then it gets compiled to some extent that depends on the implementation, then it gets run. Some implementations run every expression you type through the full native-code compiler, but others just interpret the expression, expanding any macros as they encounter them. In the latter case, it's switching back and forth between "run time" and "compile time" potentially many times during the interpretation of a single expression.

It is certainly true that macros are a different kind of code from most ordinary code, because what they're doing is different. But importantly, they are written in the same Turing-complete language. I once wrote a program (a C implementation for Lisp Machines) that allowed the user to interactively execute C expressions. Obviously, it had to parse the C code according to the syntax of that language, but the parser produced list structure that consisted entirely of macro calls. The guts of the compiler were implemented as a collection of macros that translated the output of the parser into Lisp Machine Lisp. These macros did things like accessing and updating a symbol table — far beyond what you could do in a C macro.

Re:Why Lisp macros are cool, a Perl perspective (2005) by lispm on Monday December 16, 2019 @09:17PM (#21809847)
I can compile a file to machine code and load it into another process. The file compiler provides the 'compile-time'. The other process then provides 'load-time' and 'run-time'.
Re:Why Lisp macros are cool, a Perl perspective (2005) by ScottBurson on Monday December 16, 2019 @11:01PM (#21810406)
Right, you can also do it that way. I should have said "all these things can happen in a single process in an interleaved manner".
Re:Why Lisp macros are cool, a Perl perspective (2005) by ScottBurson on Tuesday December 17, 2019 @04:02AM (#21811905)
If anyone's still reading, I can't resist adding that I was doing type checking by macro expansion — a technique that was the subject of a 2017 POPL paper [0] — in 1983.

[0] https://www.ccs.neu.edu/home/stchang/pubs/ckg-popl2017.pdf

Re:Why Lisp macros are cool, a Perl perspective (2005) by zeveb on Tuesday December 17, 2019 @09:03AM (#21813483)
That's so cool! Yet another example of Lisp enabling what other languages get in the way of.
Re:Why Lisp macros are cool, a Perl perspective (2005) by lidHanteyk on Monday December 16, 2019 @08:27PM (#21809575)
Then today you learn that Paul Graham is a tool.

There are Lisps that improve on the situation. In particular, vau calculi/fexprs are worth examining. http://lambda-the-ultimate.org/node/4093

There are also systems like Forths, where the compiler and interpreter really are the same system, in two different modes of operation. In these systems, there is only runtime; compiletime is a specific style of runtime.

Re:Why Lisp macros are cool, a Perl perspective (2005) by kbp on Monday December 16, 2019 @08:47PM (#21809698)
Could you expand on how Forth differs from Lisp? I don't know Forth.
Re:Why Lisp macros are cool, a Perl perspective (2005) by lidHanteyk on Monday December 16, 2019 @09:35PM (#21809986)
In Forth, the interpreter reads tokens. By default, each token is read and then executed. There is a token that switches the interpreter into "compiling" mode. When in compiling mode, each token is read and then used to instruct the compiler to emit code representing the token. There is a traditional and beautiful interplay [0] between the Forth interpreter and its initial stream of tokens, as the tokens customize the interpreter and compiler by augmenting their behaviors.

The main contrast that I would draw between Forths and Lisps is syntax. Forths don't really have syntax; they have token-parsing streams. Lisps are extremely tree-oriented, but Forths are stack-oriented.

[0] https://github.com/nornagon/jonesforth

Re:Why Lisp macros are cool, a Perl perspective (2005) by erik_seaberg on Tuesday December 17, 2019 @01:36AM (#21811221)
Forth has two singleton stacks, and each has a DSL. Sometimes I wonder what Forth would be like with stacks as a first-class type. Maybe actors each with their own stack …
Re:Why Lisp macros are cool, a Perl perspective (2005) by procparam on Monday December 16, 2019 @06:42PM (#21808816)
A separate language? How do you mean?

This is one of the big advantages of Lisp macros over C macros. In Lisp you write macros using Lisp, including normal user-defined functions. In C, on the other hand, you write macros using "C Preprocessor directives"

Re:Why Lisp macros are cool, a Perl perspective (2005) by Rerarom on Monday December 16, 2019 @06:51PM (#21808910)
Yes, of course, I get that it has the same syntax, which is not the case with C macros and which makes macros way better. Thing is, there is one Lisp that executed at compile time which expands the macros, and then there is the resulting code which executed at runtime. It's not self-modifying code. It's just code that operates on some other code, written in the same syntax.
Re:Why Lisp macros are cool, a Perl perspective (2005) by jlarocco on Monday December 16, 2019 @08:13PM (#21809497)
That's not true, though.

When running in the REPL (the most common way to interface with Lisp) the compile time and runtime Lisps are the same.

For example, here's a copy/paste from a REPL session that defines a macro that defines a function. The macro (at "compile time") prints information about the function (just its argument count) to stdout before using defun (itself a macro) to actually define the function.

Next I call the new function and print out a disassembly, just to show the function is in fact compiled

"CL-USER> " is the prompt in the REPL I use:

    CL-USER> (defmacro my-defun (name arguments &body body)
               (format t "Defining ~a, taking ~a arguments~%" name (length arguments))
               `(defun ,name ,arguments ,@body))
    MY-DEFUN
    CL-USER> (my-defun omg-2 (value) (* value value))
    Defining OMG-2, taking 1 arguments
    OMG-2
    CL-USER> (omg-2 34)
    1156
    CL-USER> (disassemble #'omg-2)
    ; disassembly for OMG-2
    ; Size: 33 bytes. Origin: #x52ED2714                          ; OMG-2
    ; 14:       498B5D10         MOV RBX, [R13+16]                ; thread.binding-stack-pointer
    ; 18:       48895DF8         MOV [RBP-8], RBX
    ; 1C:       488BD6           MOV RDX, RSI
    ; 1F:       488BFE           MOV RDI, RSI
    ; 22:       FF1425C0001052   CALL QWORD PTR [#x521000C0]      ; GENERIC-*
    ; 29:       488B75F0         MOV RSI, [RBP-16]
    ; 2D:       488BE5           MOV RSP, RBP
    ; 30:       F8               CLC
    ; 31:       5D               POP RBP
    ; 32:       C3               RET
    ; 33:       CC10             INT3 16                          ; Invalid argument count trap
    NIL
    CL-USER>

It is also possible to compile a Lisp program to an executable (or byte code or whatever) and run it, and not use the dynamic aspect of it.
Re:Why Lisp macros are cool, a Perl perspective (2005) by lispm on Monday December 16, 2019 @09:24PM (#21809903)
The code gets compiled (and macro expanded), before it is running. That's a definition of compile-time in Lisp.

In an interpreter version of Lisp, the interpreter may expand the macros at runtime.

Re:Why Lisp macros are cool, a Perl perspective (2005) by btilly on Monday December 16, 2019 @07:04PM (#21809025)
Whatever artificial division you believe exists between "compile time" and "run time" in Lisp is almost certainly your misunderstanding, and does not reflect how the language actually works.
Re:Why Lisp macros are cool, a Perl perspective (2005) by threatofrain on Monday December 16, 2019 @07:26PM (#21809211)
Perhaps I could use some clarity on this matter too, and hopefully with a bit of gentleness. As I understand it, some Lisps do have an explicit compile phase and there is discussion among the language users over the runtime cost of macro abstractions.

https://docs.racket-lang.org/guide/stx-phases.html

I understand there may be some philosophical or theoretical interest in framing Lisp the way you do, but does that invalidate the close-to-the-app thinking about the costs of macros and where those costs are distributed?

Re:Why Lisp macros are cool, a Perl perspective (2005) by ludston on Monday December 16, 2019 @09:14PM (#21809833)
I'm not so familiar with scheme, but I can perhaps give useful answer regarding how this works with SBCL (Probably the most well known common lisp implementation).

In SBCL, we would commonly say that code is "evaluated" rather than compiled or interpreted. This is because "compiler" is referring specifically to when assembly is emitted and "interpreter" is referring to when code is being used to call already compiled code. In the case of SBCL, it is doing both of these things simultaneously when it is reading a ".lisp" file. For example, if this is the contents of my .lisp file:

(defun boop () (print "hello"))

(boop) ;; prints hello

(defun scoop () (print "goodbye"))

The boop function is being evaluated (and therefore compiled), and then called on the next line. In traditional algol based languages like c and java with a separate compilation phase, it would not be possible to call boop before scoop has been turned into machine code.

So you are asking how the costs of evaluating lisp are distributed?

For one, the assembler generated whilst evaluating the .lisp file can be cached in a .fasl file so that it does not need to be evaluated again to be loaded into other projects.

Another is that macro's can be evaluated once during function definition. e.g.

(defmacro moob () `(print "kellog"))

(defun foo () (moob)) ; evaluates to (defun funky () (print "kellog"))

(defmacro moob () `(print "fish")) ; redefining moob

(foo) ; will print "kellog" because funky was evaluated before moob was redefined.

In this case, the cost of calling moob is trivial because it only occurs once during the definition of foo. It will slow down the initial load of your program, but no subsequent calls to foo.

Re:Why Lisp macros are cool, a Perl perspective (2005) by lispm on Monday December 16, 2019 @09:27PM (#21809936)
> This is because "compiler" is referring specifically to when assembly is emitted

SBCL has both an in-core compiler (compiles to machine code in memory) and a file compiler (compiles to machine code in an file).

SBCL also has an interpreter (relatively new and which usually is not used), which executes Lisp source as data.

Re:Why Lisp macros are cool, a Perl perspective (2005) by omaranto on Monday December 16, 2019 @09:56PM (#21810089)
I didn't know SBCL had an interpreter! (For those who didn't know either, see [1].) When would you use it?

[1] http://www.sbcl.org/manual/#Interpreter

Re:Why Lisp macros are cool, a Perl perspective (2005) by lispm on Monday December 16, 2019 @10:10PM (#21810162)
In other implementations, interpreters are used for tools like source steppers or custom tracers...
Re:Why Lisp macros are cool, a Perl perspective (2005) by ImprobableTruth on Monday December 16, 2019 @07:50PM (#21809371)
I wouldn't say they are a seperate language, but they definitely feel like a seperate layer. They're not first class objects like functions (so you can't pass them to functions) and you can't evaluate the arguments (since macros are only directly present during compilation/interpretation), it's purely a mapping from source code to source code. For example, you can't rewrite a list based on it's values or form. You can rewrite the list to a program that does the transformation when run, but the seperation feels very tangible to me. Something like FEXPRs would be closer to being "just the same as normal code", but there's good reason why macros are the way they are.
Re:Why Lisp macros are cool, a Perl perspective (2005) by samatman on Monday December 16, 2019 @10:24PM (#21810231)
You can absolutely pass a macro to a function. It's a list! What you can't do is use funcall or apply on it, because it isn't a function.

What you do is, you use macroexpand-1, like so:

https://stackoverflow.com/questions/50754347/macro-with-a-li...

Re:Why Lisp macros are cool, a Perl perspective (2005) by ScottBurson on Monday December 16, 2019 @11:13PM (#21810485)
I don't know what your threshold for "cool" is, but an example of something that's straightforward in Lisp is if you want to generate a hierarchy of class declarations from a schema, so the classes have certain instance variables and methods defined in some regular way; for instance, you might want to auto-generate a method for each class that returns a collection of the values of certain of its instance variables. In most any language other than Lisp, you'd have to define your own schema syntax and write a parser for it and a generator that outputs the declarations in the target language. In a nutshell, it would probably be painful enough that you wouldn't bother; you'd just type in all the boilerplate manually. In Lisp, not only is writing such a schema translator relatively straightforward, but you get incremental recompilation for free.