Live data from Hacker News

Lisp moving Forth moving Lisp

letoverlambda.com

21–30 of 37 posts

Re: Lisp moving Forth moving Lisp

#21

Earlier quoted context omitted.

That's true but it's not the most important aspect of macros. Macros let you define a new language that describes your problem solution more directly than you can in raw Lisp. In other words they let you create a Domain-Specific Lamguage (DSL). You can create DSLs in other languages too but it's so difficult that it's uncommon. In Lisp it's so easy that DSLs are a primary programming technique.

I don't think that moving the evaluation to compile time is something you can just ignore when discussing macros. It's an important differentiator between macros and Fexprs[1]. IME, the primary use case for macros in Lisps is language extension, a step before a DSL. For every `loop` there are thousands of `with-x` macro implementations. See the macro-writing macros in Alexandria/Serapeum for the most common patterns.…

In the case of Prolog, term and goal expansion are arbitrary compile-time execution mechanisms, completely isomorphic with macros, and are referred to as macro mechanisms.

The characterization of it not being "compile-time macro execution" is strange, what do you think these predicates are doing?

Re: Lisp moving Forth moving Lisp

#22

Earlier quoted context omitted.

I don't think that moving the evaluation to compile time is something you can just ignore when discussing macros. It's an important differentiator between macros and Fexprs[1]. IME, the primary use case for macros in Lisps is language extension, a step before a DSL. For every `loop` there are thousands of `with-x` macro implementations. See the macro-writing macros in Alexandria/Serapeum for the most common patterns.…

In the case of Prolog, term and goal expansion are arbitrary compile-time execution mechanisms, completely isomorphic with macros, and are referred to as macro mechanisms. The characterization of it not being "compile-time macro execution" is strange, what do you think these predicates are doing?

Good point. I was thinking about meta-interpreters (and it's still possible I misunderstood or misremembered how they work!), but you're right; I should have at least been more precise.

Re: Lisp moving Forth moving Lisp

#23

Earlier quoted context omitted.

That's true but it's not the most important aspect of macros. Macros let you define a new language that describes your problem solution more directly than you can in raw Lisp. In other words they let you create a Domain-Specific Lamguage (DSL). You can create DSLs in other languages too but it's so difficult that it's uncommon. In Lisp it's so easy that DSLs are a primary programming technique.

I don't think that moving the evaluation to compile time is something you can just ignore when discussing macros. It's an important differentiator between macros and Fexprs[1]. IME, the primary use case for macros in Lisps is language extension, a step before a DSL. For every `loop` there are thousands of `with-x` macro implementations. See the macro-writing macros in Alexandria/Serapeum for the most common patterns.…

Thanks for linking to Fexpr's. I've thinking about "functions with lazily evaluated arguments" recently, and trying to understand how they are different from macros. So I was basically thinking about fexprs.

I have some reading to do, but overall it seems like macros are favored instead of fexprs. Macros completely avoid some environment handling issues.

Re: Lisp moving Forth moving Lisp

#24

Earlier quoted context omitted.

Generally, it enables type-safe, ahead-of-time compiled code to perform work at compile time that would require runtime execution in many other languages

That's true but it's not the most important aspect of macros. Macros let you define a new language that describes your problem solution more directly than you can in raw Lisp. In other words they let you create a Domain-Specific Lamguage (DSL). You can create DSLs in other languages too but it's so difficult that it's uncommon. In Lisp it's so easy that DSLs are a primary programming technique.

I don’t disagree, but I think this is a matter of taste: i.e. which aspect you’d consider more important.

You can create DSLs without macros, for example by parsing and interpreting at runtime, but macros let you move much of that logic to compile time.

For me, that's the killer feature: writing code that looks dynamic or interpreted, but ends up as compiled code.

Re: Lisp moving Forth moving Lisp

#26
post #6

Earlier quoted context omitted.

all the things, if macro system is sufficiently sophisticated (ie., hygienic with ability to attach metadata to syntax object). Racket is one of languages with such system, and they implemented static typing using them.

Interestingly, Let Over Lambda eschews hygienic macros because they limit the power (and danger) inherent in macro design.

what I meant, the macro system should support hygiene, not necessarily that all macros in it must be hygienic. The syntax bits being manipulated come with an extra scope context, which means you get better preserved information about initial structure of your program; the more information macro has, the more it can achieve. And crucially, in a macro system capable of hygiene, you're not implicitly losing unhygienic capabilities. Implementing unhygienic macros in a hygienic system just means deleting all the scope information. Implementing hygienic macros in unhygienic system is a question mark. As (a somewhat imprecise) analogy, consider dynamic vs lexical scoping. If you have just lexical scoping support, you can mimic dynamic scoping with globals (since global scope is what's left when you delete other scopes). If you have just dynamic scoping support, implementing lexical scope is a question mark.

What Let Over Lambda calls limited power, is probably in reference narrowly to `syntax-rules`. It's not just that it's hygienic, but that it purposefully has no way to opt out of it (unhygienic capabilities in a hygienic system are trivial to provide; but they still need to be provided by language implementation) but what's even more limiting is that it doesn't let you write procedural macros, it's all arcane pattern matching and templates. `syntax-case` (part of r6rs) doesn't have these issues.

Re: Lisp moving Forth moving Lisp

#27

Earlier quoted context omitted.

I don't think that moving the evaluation to compile time is something you can just ignore when discussing macros. It's an important differentiator between macros and Fexprs[1]. IME, the primary use case for macros in Lisps is language extension, a step before a DSL. For every `loop` there are thousands of `with-x` macro implementations. See the macro-writing macros in Alexandria/Serapeum for the most common patterns.…

Thanks for linking to Fexpr's. I've thinking about "functions with lazily evaluated arguments" recently, and trying to understand how they are different from macros. So I was basically thinking about fexprs. I have some reading to do, but overall it seems like macros are favored instead of fexprs. Macros completely avoid some environment handling issues.

The only language with Fexprs that I used was Io. In Io, unevaluated code is represented as a tree of Message nodes, and for each call, an activation record is instantiated and provided to the called method body. That reified Call object lets you access the caller's environment and the raw Message chains passed as arguments. You can then decide whether to evaluate those messages, which ones, how many times, and in what context/environment. It's a very niche language, but if you want to see Fexprs "in action", that's the best place to look at (at least to my knowledge) - they are central to Io's design rather than an advanced feature nobody touches. It's even mentioned on the front page[1]:

> Messages as Code — Messages form trees that can be inspected and rewritten at runtime. Argument evaluation can be deferred, so if, while, and for are implementable in Io itself.

[1] https://iolanguage.org/

Re: Lisp moving Forth moving Lisp

#29
I'm not fluent enough in Common Lisp for that book, but the patterns in Let over Lambda translate cleanly to Julia. You can have "Let over Lambda", "Let over Lambda over Let over Lambda", "Anaphoric Macros" (though you have to break hygiene with esc(), and in practice should just use the native julia do block), "Reader Macros" (via String Macros), "Pandoric Macros", and so on.

Re: Lisp moving Forth moving Lisp

#30

Earlier quoted context omitted.

That's true but it's not the most important aspect of macros. Macros let you define a new language that describes your problem solution more directly than you can in raw Lisp. In other words they let you create a Domain-Specific Lamguage (DSL). You can create DSLs in other languages too but it's so difficult that it's uncommon. In Lisp it's so easy that DSLs are a primary programming technique.

I don't think that moving the evaluation to compile time is something you can just ignore when discussing macros. It's an important differentiator between macros and Fexprs[1]. IME, the primary use case for macros in Lisps is language extension, a step before a DSL. For every `loop` there are thousands of `with-x` macro implementations. See the macro-writing macros in Alexandria/Serapeum for the most common patterns.…

> I don't think that moving the evaluation to compile time is something you can just ignore when discussing macros.

Sure but you've also got CTFE in languages that lack a proper macro system such as C++. The defining characteristic (IMO) is the access to and ease of manipulating the AST on the fly. And any time you're outputting an AST you're going to need access to the compiler at which point the line between run time and compile time becomes blurred and arbitrarily nested.

Post reply on HN