[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)