[HN Gopher] Generalized Macros
       ___________________________________________________________________
        
       Generalized Macros
        
       Author : ianthehenry
       Score  : 80 points
       Date   : 2023-04-18 13:51 UTC (1 days ago)
        
 (HTM) web link (ianthehenry.com)
 (TXT) w3m dump (ianthehenry.com)
        
       | kerkeslager wrote:
       | This post persuaded me to not take much of a look at Janet. This
       | is also related to why I think that Lisps haven't become the most
       | widely-used programming languages.
       | 
       | Lisp enthusiasts like to point out the power of macros, and
       | macros are the raison d'etre for Lisp's homogeneous s-expression
       | syntax. Most other features in Lisp (such as first-class closures
       | and higher-order functions) can exist without s-expressions, but
       | the powerful thing about s-expressions is that they enable Lisp
       | macros.
       | 
       | But with great power comes great responsibility. When I'm writing
       | a program I want as little responsibility as possible while still
       | being able to solve the problem at hand. I don't want to be
       | responsible for memory management and bounds checking, and I
       | don't want to be responsible for the hygiene of my macros at both
       | the definition and the call site.
       | 
       | With C, the responsibility of memory management and bounds
       | checking comes with a power that people actually need to solve
       | problems. For me these problems usually come up in the context of
       | writing my own hobbyist interpreters/compilers, but there are a
       | lot of real world cases where these come up. But often you _don
       | 't_ need the capabilities of C, and I'd argue as a result that
       | there are a lot of cases where using C is a bad idea because it's
       | not the best way to solve your problem.
       | 
       | And here's the hot take: the power of Lisp macros isn't actually
       | ever worth the responsibility in my experience. The problem Lisp
       | macros solve is "this code is more
       | verbose/ugly/boilerplate-y/etc. than I want it to be", which just
       | isn't the problem you're writing the program to solve. Whenever
       | you reach for a macro, there's another tool you could be reaching
       | for to solve the actual problem at hand. At the very least, you
       | can just write the code the macro would expand into. There's
       | inherently never a case where the macro is the only way to solve
       | the problem.
       | 
       | If you're good at writing macros, you won't always get burned by
       | them, but nobody is ever perfect at writing macros, so everyone
       | gets burned sometimes. If you're writing software that you
       | actually need to work, the risk is rarely worth it.
       | 
       | When I have written production code in a Lisp (mostly Clojure)
       | I've rarely reached for macros, and often bugfixes have been
       | removing a macro that was of the "so preoccupied with whether or
       | not they could, that they didn't stop to think if they should"
       | variety. And if you spend enough time avoiding and removing
       | macros, you start to wonder why you're destroying your eyesight
       | trying to match parentheses, when the entire reason for the
       | parentheses is to enable something you have to avoid and remove.
       | 
       | And don't get me wrong: macros are _cool_.  "Because I like them"
       | is a totally valid reason to write macros and Lisp.
        
         | Zababa wrote:
         | The author wrote a whole book on Janet, with 11 of the 13
         | chapters focusing on something else than macros:
         | https://janet.guide/. I've been reading it in the last few days
         | and it's very nice, there are lots of things to play around
         | with outside of macros.
        
         | harryvederci wrote:
         | > you start to wonder why you're destroying your eyesight
         | trying to match parentheses
         | 
         | Manually matching parentheses is a non-issue since tools like
         | Parinfer were created: https://shaunlebron.github.io/parinfer
        
           | kerkeslager wrote:
           | Parinfer only helps you write parentheses, it doesn't help
           | you read them.
        
         | blatant303 wrote:
         | (defmacro ->           "Threads the expr through the forms.
         | Inserts x as the           second item in the first form,
         | making a list of it if it is not a           list already. If
         | there are more forms, inserts the first form as the
         | second item in second form, etc."           {:added "1.0"}
         | [x & forms]           (loop [x x, forms forms]             (if
         | forms               (let [form (first forms)
         | threaded (if (seq? form)                                (with-
         | meta `(~(first form) ~x ~@(next form)) (meta form))
         | (list form x))]                 (recur threaded (next forms)))
         | x)))
        
         | foodoos wrote:
         | > the power of Lisp macros isn't actually ever worth the
         | responsibility in my experience
         | 
         | Expand your experience.
         | 
         | > At the very least, you can just write the code the macro
         | would expand into.
         | 
         | You can "just" get it wrong when the code is used in many
         | places.
         | 
         | Consider this as the hot take: macros reduce the number of
         | characters you have to type. I haven't seen anything that beats
         | macros in this metric.
        
           | kerkeslager wrote:
           | > Expand your experience.
           | 
           | That's universally good advice, which you should follow too.
           | 
           | But neither of us is going to experience all that exists to
           | experience, so it makes sense to prioritize. I've experienced
           | enough pain debugging Lisp macros to make an educated
           | decision to deprioritize further exploration in that area.
           | 
           | > You can "just" get it wrong when the code is used in many
           | places.
           | 
           | True, which is why I generally use other abstractions to
           | avoid repeating myself.
           | 
           | > Consider this as the hot take: macros reduce the number of
           | characters you have to type. I haven't seen anything that
           | beats macros in this metric.
           | 
           | Sure, but within reasonable languages that's not a metric
           | which matters. Typing has never been the bottleneck of my
           | development. Even in absurdly verbose languages like C++,
           | IDEs generate a lot of the code for you: the bottlenecks in
           | C++ are things like stitching together different ways of
           | doing the same thing which were used in libraries or
           | different parts of the codebase.
        
             | foodoos wrote:
             | I appreciate and I'm impressed you're gratuitously
             | positive.
             | 
             | I think you mean within reasonable _programs_ that 's not a
             | metric that matters because most programs solve problems
             | that don't need much autogeneration to be solved. For
             | problems that do need autogeneration macros are one of the
             | strongest ways.
             | 
             | A good way to become convinced of the power of macros is to
             | try and write a web app in Arc to generate lots of HTML.
             | 
             | IDEs that generate a lot of the code are proof macros are
             | needed because when you want to change the autogenerated
             | code you have to do it by hand. Autogeneration can be done
             | with macros and is simpler with macros.
        
               | kerkeslager wrote:
               | > I think you mean within reasonable programs that's not
               | a metric that matters because most programs solve
               | problems that don't need much autogeneration to be
               | solved. For problems that do need autogeneration macros
               | are one of the strongest ways.
               | 
               | No, I meant languages (as in programming languages), not
               | programs.
               | 
               | > A good way to become convinced of the power of macros
               | is to try and write a web app in Arc to generate lots of
               | HTML.
               | 
               | I'm already convinced macros are powerful. The problem is
               | that they're also extremely error prone, and can be
               | extremely difficult to debug when errors inevitably
               | occur.
               | 
               | This isn't controversial. Land Of Lisp[1] has a section
               | titled "Macros: Dangers and Alternatives". The elisp
               | docs[2] contain a section on "Common Problems Using
               | Macros". Paul Graham's _On Lisp_ [3] contains a chapter
               | on variable capture which consists mostly of sections on
               | avoiding variable capture problems, followed by a chapter
               | called _Other Macro Pitfalls_.
               | 
               | These are the people who _like_ Lisp writing these
               | things. The controversial thing I 'm saying is that I
               | don't think the power/danger tradeoff is worth it. That's
               | pretty subjective and it's reasonable to disagree with
               | that, but you can't reasonably disagree that macros are
               | error prone when even the people writing Lisp variants
               | are saying they are error-prone.
               | 
               | The best HTML generation I've experienced is with Ruby
               | templates. They're not as terse as Lisp macros, but I
               | don't end up having to debug them very often, and when I
               | do end up debugging them, the bugs are usually trivial to
               | find and fix.
               | 
               | > Autogeneration can be done with macros and is simpler
               | with macros.
               | 
               | I have never spent hours debugging an IDE autocomplete. I
               | have spent many hours debugging macros.
               | 
               | You're only looking at the positives of macros and
               | ignoring everything I've said about the negatives.
               | 
               | [1] https://www.oreilly.com/library/view/land-of-
               | lisp/9781593272...
               | 
               | [2] https://www.gnu.org/software/emacs/manual/html_node/e
               | lisp/Pr...
               | 
               | [3] https://redirect.cs.umbc.edu/courses/331/fall10/resou
               | rces/li...
        
               | foodoos wrote:
               | Thanks for sharing your experience with Ruby templates
               | and for the links.
               | 
               | > I don't think the power/danger tradeoff is worth it.
               | 
               | The power/danger tradeoff might not be worth it for the
               | overwhelming majority of programs. For some kinds of
               | programs I don't see how this power can be got with
               | anything less powerful than macros.
        
           | codr7 wrote:
           | More importantly, they reduce the number of characters you
           | have to read and maintain.
           | 
           | Macros allow writing code that's both compact and readable
           | [0], in lesser languages you have to choose.
           | 
           | [0] https://github.com/codr7/cl-
           | redb/blob/ff3a34a31ced7a9668fc95...
           | 
           | [1] https://gist.github.com/codr7/4bb9442c0c66411643eddd8db01
           | 64a...
        
             | kerkeslager wrote:
             | > More importantly, they reduce the number of characters
             | you have to read and maintain.
             | 
             | So does giving all your variable names one-letter names and
             | putting your entire codebase on one line. Surely we can
             | agree that character count is a poor measure of readability
             | or maintainability.
             | 
             | > Macros allow writing code that's both compact and
             | readable [0], in lesser languages you have to choose.
             | 
             | Macros aren't the only abstraction that does that, even
             | within Lisp.
        
               | martinflack wrote:
               | > So does giving all your variable names one-letter names
               | and putting your entire codebase on one line. Surely we
               | can agree that character count is a poor measure of
               | readability or maintainability.
               | 
               | Which is why pg advocates symbol count. (Although he
               | seems to love brevity too.)
        
         | lispm wrote:
         | > At the very least, you can just write the code the macro
         | would expand into
         | 
         | Whenever you write code, you could just write the function it
         | calls.
         | 
         | One purpose of macros is to create descriptive notations.
         | That's one way to create very readable, maintainable code, ...
         | Lisp itself uses macros in the language everywhere: to define
         | functions, to define classes, to define namespaces, to provide
         | control structures, ... the core special operators are at a
         | minimum and the large amount of syntactic operators are written
         | as macros. As a developer I can use the same power to shape the
         | operators of my domain, beyond what functions offer me and with
         | compile time efficiency & syntactic control/abstraction.
        
           | kerkeslager wrote:
           | > Whenever you write code, you could just write the function
           | it calls.
           | 
           | This is overly-glib. Calling a function doesn't come with the
           | risk that calling a macro does, which I'm sure someone named
           | "lispm" is aware of.
           | 
           | > Lisp itself uses macros in the language everywhere: to
           | define functions, to define classes, to define namespaces, to
           | provide control structures, ... the core special operators
           | are at a minimum and the large amount of syntactic operators
           | are written as macros. As a developer I can use the same
           | power to shape the operators of my domain, beyond what
           | functions offer me and with compile time efficiency &
           | syntactic control/abstraction.
           | 
           | This just shows you don't understand the power/responsibility
           | argument you're responding to.
           | 
           | As a developer I don't have the resources to design and test
           | my code to the extent that the language creators do.
           | 
           | Lisp itself uses direct memory access to define the garbage
           | collector, and you could use the same power to shape the
           | memory usage of your domain, beyond what garbage collection
           | offers and with compile time efficiency. But surely we can
           | agree that's a terrible idea in most cases.
        
             | lispm wrote:
             | There is a lot of irrational fear of macros.
             | 
             | > As a developer I don't have the resources to design and
             | test my code to the extent that the language creators do.
             | 
             | Sure you could wait many years for the 'language designer'
             | to provide you with new syntactical support or just write
             | it in an afternoon yourself. It's the same crazy idea as
             | writing a new class with its methods, instead just using
             | the pre-made class hierarchy from the language or library
             | designer.
             | 
             | Surprise: programming macros can be learned and it does not
             | hurt.
             | 
             | > But surely we can agree that's a terrible idea in most
             | cases.
             | 
             | Not sure who this we is, but it is not me.
             | 
             | It's exactly what the Lisp Machine did: you could use the
             | same power to shape the memory usage of your domain. It had
             | a bunch of different garbage collectors working side by
             | side and a programmable memory management. All that was
             | written in Lisp itself.
        
               | kerkeslager wrote:
               | > There is a lot of irrational fear of macros.
               | 
               | I'm just going to copy from my other post:
               | 
               | Land Of Lisp[1] has a section titled "Macros: Dangers and
               | Alternatives". The elisp docs[2] contain a section on
               | "Common Problems Using Macros". Paul Graham's _On Lisp_
               | [3] contains a chapter on variable capture which consists
               | mostly of sections on avoiding variable capture problems,
               | followed by a chapter called _Other Macro Pitfalls_.
               | 
               | I suppose it's possible that the authors of Lisp dialects
               | are being irrational...
               | 
               | > Sure you could wait many years for the 'language
               | designer' to provide you with new syntactical support or
               | just write it in an afternoon yourself. It's the same
               | crazy idea as writing a new class with its methods,
               | instead just using the pre-made class hierarchy from the
               | language or library designer.
               | 
               | Given all the work that has been done in modern languages
               | to avoid the pitfalls of classes such as multiple
               | inheritance, this is a better example of my point than
               | you think it is.
               | 
               | > Not sure who this we is, but it is not me.
               | 
               | > It's exactly what the Lisp Machine did: you could use
               | the same power to shape the memory usage of your domain.
               | It had a bunch of different garbage collectors working
               | side by side and a programmable memory management. All
               | that was written in Lisp itself.
               | 
               | The Lisp Machine is not "most cases". It's unsurprising
               | that an attempt to run Lisp directly on hardware would
               | need to do some direct memory access, but it's also
               | absurd to extrapolate that to claim that this is
               | something that is a good idea in most cases. Your average
               | Lisp program does not and _should not_ directly manage
               | memory, and claiming otherwise is obviously just trying
               | to win a silly point. I don 't believe for a second that
               | you do direct memory access in your Lisp programs on a
               | regular basis, unless you're writing some very low-level
               | stuff like hardware or a Lisp compiler/interpreter, and
               | again, that's not "most cases". You're simply making a
               | disingenuous argument here.
               | 
               | [1] https://www.oreilly.com/library/view/land-of-
               | lisp/9781593272...
               | 
               | [2] https://www.gnu.org/software/emacs/manual/html_node/e
               | lisp/Pr...
               | 
               | [3] https://redirect.cs.umbc.edu/courses/331/fall10/resou
               | rces/li...
        
               | lispm wrote:
               | > Macros: Dangers and Alternatives
               | 
               | here is a simple LispWorks program using macros to
               | provide manual memory management:                 CL-USER
               | 17 > (defresource rarray (size)
               | :constructor (make-array size)
               | :initial-copies 0)       RARRAY
               | 
               | The above macro form defines a new manually managed
               | resource named RARRAY. There is a constructor for
               | allocating new RARRAY objects. Here the constructor just
               | allocates an array of a certain size.                 CL-
               | USER 18 > (using-resource (ra rarray 10)
               | (setf (aref ra 3) 10)                      (incf (aref ra
               | 3) 32)                      (aref ra 3))       42
               | 
               | The above USING-RESOURCE macro looks up an array of size
               | 10 from the resource pool. If there is none, it allocates
               | one. In the context of the macro form, the array is
               | marked in the pool as used and provided to the code. Upon
               | leaving the dynamic context of USING-RESOURCE, the array
               | will be returned to the pool and marked as unused.
               | 
               | Bonus: DEFRESOURCE, USING-RESOURCE, SETF and INCF are all
               | macros.
        
         | cfiggers wrote:
         | > This post persuaded me to not take much of a look at Janet.
         | 
         | Do you mean to say that you _were_ intent on taking a good look
         | at Janet when you woke up this morning, but because of _this
         | post specifically_ your mind has been radically changed?
        
           | kerkeslager wrote:
           | Eh, "intent" and "radically" are strong words, but it was on
           | the back burner as something to look into further because a
           | previous post had mentioned the macro system was a bit
           | different, and now it's not.
        
       | sparkie wrote:
       | Interesting idea. I'll play around with when I get home.
       | 
       | I use Kernel a lot, which allows you to write first-class
       | operatives which can influence the bindings of their caller, but
       | they don't allow modifying the body of the calling function.
        
       | openasocket wrote:
       | Macros tickle a certain peculiar part of my brain. I have this
       | idea of making a lisp that at its core is just some static single
       | assignment language, or some other type of IR. And then all the
       | traditional control flow features would be implemented using
       | macros. You could even implement optimization passes as macros. I
       | haven't been brave enough to actually try it, but I imagine if I
       | did I would need some more powerful macro features like this.
        
       | remexre wrote:
       | I wonder if this might be made even more powerful by using a
       | zippers instead of a single left-right pair; then, the
       | generalized-macro could traverse the entire top-level form?
        
         | ianthehenry wrote:
         | I think that a zipper would provide a much nicer interface for
         | making "distant" transformations, but just to be clear you
         | _can_ traverse the entire AST with this approach by recursively
         | returning macros that return macros and then re-assembling the
         | entire tree once you get where you 're going. Just, ah, not the
         | easiest code to write :)
        
       | hzhou321 wrote:
       | LISP has been singing homoiconicity as its feature. I lately
       | start to think homoiconicity is really a wart. The macros are a
       | system to program the code, while the code is a system to program
       | the application. They are two different cognition tasks and
       | making them nearly indistinguishable is not ideal.
       | 
       | LISP has a full-featured macro system, thus hands down beats many
       | languages that only possess handicapped macro system or no macro
       | system at all. It uses the same/similar language to achieve it is
       | mere accidental. In fact, I think LISP is an under-powered
       | programming language due to its crudeness. But it's unconstrained
       | macro system allows it compensate the programming part to certain
       | degree. As a result, it is not a popular language and it will
       | never be, but it is sufficiently unique and also extremely simple
       | that it will never die.
       | 
       | What if, we have a standalone general-purpose macro system that
       | can be used with any programming languages, with two syntax layer
       | that programmers can put on different hat to work on either?
       | That's essentially how I designed MyDef. MyDef supports two forms
       | of macros. Inline macros are using `$(name:param)` syntax. Block
       | macros are supported using `$call blockmacroname, params`. Both
       | are syntactically simple to grasp and distinct from hosting
       | languages that programmers can put on different hats to
       | comprehend. The basic macros are just text substitution, but both
       | inline macros and block macros can be extended with (currently my
       | choice) Perl to achieve unconstrained goals. The extension layer
       | can access the context before or after, can set up context for
       | code within or outside, thus achieve what lisp can but using
       | Perl. We can extend the macros using Python or any other language
       | as well, but it is a matter of the extent to access the macro
       | system internals.
       | 
       | Inline macros are scoped, and block macros can define context.
       | These are the two features that I find missing in most macros
       | systems that I can't live without today. Here is an example:
       | $(set:A=global scope)         &call open_context
       | print $(A)         print $(A)              subcode: open_context
       | set-up-context             $(set:A=inside context)
       | BLOCK # placeholder for user code             destroy-context
        
         | JonChesterfield wrote:
         | m4 is like that. Lots of templating languages are too. Code
         | generation is useful and roughly what you end up with if the
         | language isn't expressive enough, e.g. C or go. Is yours doing
         | anything different to those?
         | 
         | What the lisp approach gives you is the macro has the parsed
         | representation of the program available to inspect and
         | manipulate. It doesn't have to expand to some fixed text, it
         | can expand into different things based on the context of the
         | call site.
         | 
         | Various languages have decided that syntactically
         | distinguishing macros from functions is a good idea. Maybe the
         | function is foo and a macro would be $foo. For what it's worth
         | I think that's wrong-headed, and the proper fix is to have
         | 'foo' represent some transform on its arguments, where it
         | doesn't matter to the caller whether that is by macro or by
         | function, but it's certainly popular.
        
           | hzhou321 wrote:
           | M4 only do token-level macros or inline macros. M4 macros are
           | identifier that can't be distinguished from underlying
           | language. M4 macros does not have scopes. M4 does not have
           | context level macros. M4 does not have full programmable
           | ability to extend syntax.
           | 
           | I can define any macro lisp can define, just not with LISP
           | syntax. I do not have a full AST view of the code, due to the
           | its generality that it does not marry to any specific
           | underlying language. But I can have extensions that is
           | tailored to specific language and do understand the syntax.
           | For example, the C extension can check existing functions and
           | inject C functions. MyDef always can query and obtain the
           | entire view of the program, and it is up to the effort in
           | writing extensions to which degree we want macro layer to be
           | able to parse. Embedding a AST parser for a local code block
           | is not that difficult.
           | 
           | It's like the innerHTML thing, for me, I always find the text
           | layer (as string) is more intuitive for me to manipulate than
           | an AST tree. If needed, make an ad-hoc parser in Perl is
           | often simple and sufficient, for me at least.
        
         | packetlost wrote:
         | > I lately start to think homoiconicity is really a wart.
         | 
         | I sorta agree. The simplicity of the syntax and representation
         | makes it particularly amenable to dynamic modification above
         | basically every other language though. There's a bunch of other
         | properties that make this to case though, such as a very
         | dynamic type system, first-class functions, extremely simple
         | syntax, etc. so it's really a combination of a bunch of
         | factors.
         | 
         | That being said, I think homoiconicity _is_ actually a useful
         | feature, but runtime macro expansion is the dangerous part.
         | What I 'd really like to see is a Lisp with _very well defined
         | execution orders_. That is, macros must be expanded at compile
         | time, and with clear symbols for defining what runs and when. I
         | 'm not talking about something like `(macroexpand ...)`, more
         | like `(comptime (my-macro ...))`... `(define (my-func) (my-
         | macro+ 'a 'b 'c))` where it's explicit that a macro executes at
         | compile time, and usage of that macro must be denoted with a
         | special character (`+`, in my example).
         | 
         | I think a general purpose macro language is only as useful as
         | the average code-gen/templating language. It's just _string_
         | manipulation, which can be harder to reason about than true
         | metaprogramming and might result in some ugly /verbose output
         | and having to context-switch between 2 entirely different
         | languages often. What's particularly powerful about Lisp macros
         | is that usage of them doesn't look any different than a normal
         | application usually, and writing a macro is only marginally
         | different than writing a normal function.
        
           | hzhou321 wrote:
           | > I think a general purpose macro language is only as useful
           | as the average code-gen/templating language. It's just string
           | manipulation, which can be harder to reason about than true
           | metaprogramming ...
           | 
           | I would like you to reconsider. Predicting a program output
           | is hard. So in order to comprehend a macro programed as code,
           | one need run the macro in their head to predict its output,
           | then they need comprehend that output in order to understand
           | the code. I think that is unreasonable expectation. That's
           | why reasonable usage of meta-programming is close to
           | templating where programmer can reason with the generated
           | code directly from the template. For more higher-powered
           | macros, I argue no one will be able to reason with two-layers
           | at the same time. So what happens is for the programmer to
           | put on his macro hat to comprehend the macro, then simply use
           | a good "vacabulary" (macro name) to encode his comprehension.
           | And when he put his application programming hat, he takes the
           | macro by an ambiguous understanding, as a vocabulary, or some
           | one may call it as a syntax extension. Because we need put on
           | two hats at different time, we don't need homoiconicity to
           | make the two hats to look the same.
        
             | packetlost wrote:
             | Or you do:
             | 
             | ``` (require (ast macroexpand))
             | 
             | (display (macroexpand my-macro arg1 arg2)) ``` and call it
             | a day.
             | 
             | My argument isn't that the context-switch isn't there, it's
             | that the context switch will happen regardless and having
             | to think in 2 different languages is more mental overhead
             | than is necessary. I do agree that homoiconicity is _not_ a
             | requirement, but it is nice to be able to do it all in one
             | go and with no additional tooling, editor, etc.. In
             | reality, a sizeable chunk of Lisp programmers are executing
             | their code in a REPL continuously as part of their core
             | development loop, there 's no tooling (context) switch, no
             | language context switch, and barely a macro vs application
             | code context switch.
             | 
             | To illustrate, Rust macros are _basically_ that. They have
             | a _substantially_ different syntax to normal Rust code that
             | make it very difficult to quickly grok what is going on. It
             | 's a net negative IMO, not a positive.
        
               | hzhou321 wrote:
               | > To illustrate, Rust macros are basically that. They
               | have a substantially different syntax to normal Rust code
               | that make it very difficult to quickly grok what is going
               | on. It's a net negative IMO, not a positive.
               | 
               | Yeah, more like a syntax extension than macro. But I am
               | saying that you need both. Some time you need powerful
               | macro ability to extend the language. Sometime you just
               | need templating to achieve the expressiveness. With LISP,
               | I get it that you are programming all the time, never
               | templating, right? But I guess you only appreciate
               | templating when you use your macro system as a general
               | purpose system . The benefit of general purpose macro
               | systems is you only learn one tool for all languages,
               | rather than re-learn the individual wheels. And when you
               | judge a language, you no longer bothered by its syntactic
               | warts because you can always fix the expressive part with
               | your macro-layer.
        
           | hzhou321 wrote:
           | > That being said, I think homoiconicity is actually a useful
           | feature, but runtime macro expansion is the dangerous part.
           | 
           | Practically, why would you ever want a runtime macro
           | expansion?
        
             | packetlost wrote:
             | Well, part of the problem is is passing around quoted forms
             | (ie. `(quote ...)` or in may Lisps, `(...)). If that form
             | contains a macro, and a sizeable chunk of Lisps stdlibs
             | _are_ macros, you probably need to implement runtime macro
             | expansion at least partially. So in order to avoid
             | implementing runtime macro expansion, you need to break the
             | semantics of what a quoted form is, or disallow them
             | entirely. The implementation for the former could get
             | really complicated depending on the cases you want to
             | support, resolving symbols in the corresponding scope comes
             | to mind as being particularly challenging. Removing quoted
             | forms entirely just really isn 't an option, it's required
             | for one of the most powerful parts of Lisps in general:
             | built-in syntax templates. So we're back to breaking the
             | semantics of quoted forms, which I think can be done
             | reasonably, if not difficult to implement.
        
               | hzhou321 wrote:
               | In another word, it (to have runtime macro expansion) is
               | a side effect, a compromise, a wart, rather than a design
               | goal, right?
        
               | packetlost wrote:
               | It can certainly be a design goal if you want it to be.
               | Sometimes truly dynamic programming is what you want and
               | need. I, personally, wouldn't want to write any code like
               | that because I mostly write software for other people
               | that runs while they aren't looking, not for myself that
               | runs when I execute it. Lisps are popular in academic
               | settings where the program behavior is the topic of
               | interest and maximum flexibility is a tool to accelerate
               | progress. These types of scenarios usually have the
               | person who wrote the code directly involved in it's
               | execution, as opposed to service development where you
               | want to be more sure it'll actually work before you
               | deploy it.
        
             | JonChesterfield wrote:
             | Most common case is probably trying to pass `and` to
             | something that applies it to a list, `fold` or `reduce` or
             | similar, and being told some variant of "no deal, and is a
             | macro, not a function" with workarounds like `(reduce
             | (lambda (x y) (and x y)) list))`
        
       | nickdrozd wrote:
       | This post discusses a variety of Lisp macro that doesn't merely
       | expand into something else, but actually reaches out to _rewrite
       | its surrounding context_.
       | 
       | > So people have spent a lot of time thinking about ways to write
       | macros more safely - sometimes at the cost of expressiveness or
       | simplicity - and almost all recent languages use some sort of
       | hygienic macro system that defaults to doing the right thing.
       | 
       | > But as far as I know, no one has approached macro systems from
       | the other direction. No one looked at Common Lisp's macros and
       | said "What if these macros aren't dangerous enough? What if we
       | could make them even harder to write correctly, in order to
       | marginally increase their power and expressiveness?"
       | 
       | The first example discussed is a _defer_ macro that can be
       | invoked with  "no indentation increase and no extra nested
       | parentheses".
       | 
       | As a macro-lover and an indentation-hater, I think this is a
       | brilliant and hilarious idea.
        
       | munificent wrote:
       | Really interesting article. Maybe not a _good_ idea as a language
       | feature, but certainly an _interesting_ one.
       | 
       | This is my new favorite typo:                   (defmacaron .
       | lefts [key & rights]           ~(,;(drop-last lefts)
       | (get ,(last lefts) ,(keyword key))             ,;rights))
       | 
       | We're working on a macro proposal for Dart and I wonder if users
       | would like them more if we called them "macarons".
        
         | Graziano_M wrote:
         | I noticed that as well, but it's not a typo:
         | https://github.com/ianthehenry/macaroni/blob/master/src/init...
        
         | gumby wrote:
         | I am diabetic so no, I could not use this feature.
        
         | ianthehenry wrote:
         | Oh whoops! That's what I called them in my prototype so I
         | didn't shadow Janet's built-ins. Forgot to update that one when
         | I copied it to the blog post :)
        
       | foodoos wrote:
       | There's a stronger abstraction than "generalized macros" and the
       | author of this article missed it.
        
         | blatant303 wrote:
         | enlighten us please. I'm really curious.
        
           | foodoos wrote:
           | The author is reaching for a code walker.
        
           | Conscat wrote:
           | Maybe it's Racket grammars? (aka syntax-rules)
        
         | invalidOrTaken wrote:
         | care to enlighten us?
        
       | abecedarius wrote:
       | Re prior art: I'm at least vaguely reminded of "expansion-passing
       | style" by Dybvig, Friedman, and Haynes.
       | https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.64...
       | 
       | (Just from a quick skim of this long post.)
        
         | mncharity wrote:
         | Riffing on related work, _Macros for DSLs_ [1] notes non-
         | locality (and there's racket's dsl emphasis[2] in general).
         | Apropos composition, I liked the PADL'23 Modern Macros video[3]
         | (seemingly twice submitted to hn without traction).
         | 
         | [1] Macros for Domain-Specific Languages
         | https://par.nsf.gov/servlets/purl/10220787 https://docs.racket-
         | lang.org/ee-lib/index.html [2] From Macros to DSLs: The
         | Evolution of Racket
         | https://drops.dagstuhl.de/opus/volltexte/2019/10548/pdf/LIPI...
         | [3] PADL'23 Modern Macros
         | https://www.youtube.com/watch?v=YMUCpx6vhZM
        
       ___________________________________________________________________
       (page generated 2023-04-19 23:01 UTC)