Live data from Hacker News

The Problem with Macros

ianthehenry.com

51–60 of 70 posts

Re: The Problem with Macros

#51

Earlier quoted context omitted.

> I purport solving this problem is akin to solving a 0.00000001% issue It seems like Common Lisp has done quite a lot to solve this problem already, which is why it is a non-issue in Common Lisp. > non-externalizable, non-PRINTable function objects Yes, this would be a very annoying technique in a language with non-externalizable, non-PRINTable functions. > This is contrary to Lisp’s DNA of interactive development a…

It’s ambiguous to me whether you’re suggesting there are compiled languages with printable and externalizable functions. Given that many useful functions are actually closures over lexical environments possibly shared by other functions, and compiled to native code, I’d be impressed to find a language where such objects are printable and externalizable. I don’t think they are in Janet.

In s7 scheme:

  > (define f (let ((x 5)) (lambda (y) (+ x y))))
  f
  > (f 4)
  9
  > (object->string f :readable)
  "(let ((x 5)) (lambda (y) (+ x y)))"
Maintaining shared references is not necessary to meaningfully print objects. For instance, conses can be printed and read back, even though mutations will not propagate to the versions read.

Re: The Problem with Macros

#52
post #21

May be unrelated, but that's why I prefer the way JS approach this kind of problem: JS doesn't have macros. People use callback function to achieve almost the same thing function doTexture(texture, fn) { beginTextureMode(texture) fn() endTextureMode() } doTexture(myTexture, () => { drawCircle() drawRectagle() }) At the cost of being more verbose, we get the benefit of one thing less to learn; and simplicity is a powe…

I debated mentioning this explicitly, but decided it was just noise next to the rest of the post. But you should note that, while that macro can be easily replaced with lambdas without much change in ergonomics, there are lots of much more interesting things you can do with macros that do not have equivalent substitutes in a language like JavaScript. (e.g. JSX exists as its own weird pre-processor thing, but you coul…

Didn't recognize you're the OP! So my comment may not be that off-topic then :D

> But learning programming language theory is the best part :)

That's very true :)

Re: The Problem with Macros

#53
post #50

Earlier quoted context omitted.

> People use callback function to achieve almost the same thing There's a QoL difference here with macros - writing out those lambdas can become annoying. That said, the "good style" rule in (Common) Lisp is to prefer lambda-forms in such cases - i.e. cases where the macro parameters are a block of code to be mostly run straight. In fact, a common pattern for with-macros (of which doTexture would be an example), is t…

> (do-texture texture > o O r R x2) This might be another reason why I find macros less appealing: macros introduce DSL in form of normal s-expression, but they don't actually behave like a function; macros introduce their own mini-language/syntax. In the last example you provided, I bet the macro implementation would look like a little interpreter? If that's the case, having a function call like doTexture(myTexture,…

Remember JavaScript before "async"? Lots of boilerplate there. You had to wait until some committee decided to incorporate it into the language.

So "modern" languages without macros have such patches every now and then. Still, it's easy to find boilerplate in programs.

The reason is that a lot of boilerplate is specific to the program's domain, and you're left with cumbersome syntactic patterns and no tools to abstract them.

So yes, you need to learn how to write macros. But, assuming good taste, the simplicity is in their use, not their definition. It's a good tradeoff because macros are used much more often than they are defined.

By the way, the language introduced by a macro often allows arbitrary Lisp code to be mixed with it. The example did not demonstrate this, and that's why you had an easy time thinking up a non-macro alternative (which still has some unfortunate implications, like needing to interpret at runtime).

Re: The Problem with Macros

#54

As a relative Lisp novice, I think this article is excellent! I really enjoyed it, both from a technical and also an entertainment perspective. Time to put the rest of the blog post series on my ever-growing reading list…. One minor typo (I thought it was pretty funny): “it seems like it was actually a performance hack in order to make a dynamically typed language performant on the hardware of the early 1890s.” Or, d…

In his paper "History of Lisp" (PDF warning: http://jmc.stanford.edu/articles/lisp/lisp.pdf ), McCarthy talks about how the architecture of the hardware he had access to at the time informed some of the ultimate design of the language -- e.g. the now-ubiquitous "caw" and "cdw" functions were actually acronyms for "contents of the address weft" and "contents of the decrement warp," because the looms he had access to o…

People sometimes mistakenly knitpick my jokes, but I always put it down to writing too much purl.

Re: The Problem with Macros

#55
post #19
post #15

Earlier quoted context omitted.

"no notes" is an idiom meaning "I have no suggestions for improvement" The author was being sincere, rather than sarcastic as your initial parse suggests

I was not suggesting they were being sarcastic. I was suggesting they had not actually noticed that the error message actually answered their question: "Why can’t I shadow this function?". This is the section of the standard the error message points at: http://www.lispworks.com/documentation/lw71/CLHS/Body/11_aba... And this is the reasoning for why that section exists, linked to from that page: http://www.lispworks.…

Having also not spent that much quality time with the hyperspec, even if I've quite enjoyed dabbling in common lisp from time to time, it wouldn't've occurred to me that the spec would explain 'why' as well as 'what'.

"Underestimating the hyperspec" is a persistent mistake on my part sadly, although I at least seem to underestimate it less these days.

Re: The Problem with Macros

#56
post #38

Clojure alleviates this to a great extent by automatically namespace-qualifying symbols within quasiquoted forms, which prevents clashes between let-bound symbols (which are never qualified) and ones from the global scope. It’s still possible to shoot yourself in the foot (see https://blog.danieljanus.pl/2020/01/21/middleware/ for my personal story), but for that you need `binding`, `with-redefs` or `alter-var-root`,…

Time flies by when writing clojure on a train?

Re: The Problem with Macros

#57
post #7

Bravo, my friend, bravo. The unit testing macros for my Racket project were created so I could create snapshot tests for an API. The custom test-suite and test-case macros set parameters (think React context properties) for the suite and case names. There is a custom `check-snapshot` function which save snapshot files under ./snapshots/suites/{suite-name}/cases/{case-name}. It evaluates each expression and pretty-pri…

Talking about Racket and macrology, I think OP should get in touch with the PLT/Racket team. Matthew Flatt made many talks and papers about macro layers and proper scoping.

Re: The Problem with Macros

#58
post #50

Earlier quoted context omitted.

> People use callback function to achieve almost the same thing There's a QoL difference here with macros - writing out those lambdas can become annoying. That said, the "good style" rule in (Common) Lisp is to prefer lambda-forms in such cases - i.e. cases where the macro parameters are a block of code to be mostly run straight. In fact, a common pattern for with-macros (of which doTexture would be an example), is t…

> (do-texture texture > o O r R x2) This might be another reason why I find macros less appealing: macros introduce DSL in form of normal s-expression, but they don't actually behave like a function; macros introduce their own mini-language/syntax. In the last example you provided, I bet the macro implementation would look like a little interpreter? If that's the case, having a function call like doTexture(myTexture,…

I've picked a really silly example of a DSL just to illustrate the point in a few lines, but I feel the silliness is obscuring what I wanted to communicate. Next time I'll try to come up with something more useful.

> macros introduce DSL in form of normal s-expression, but they don't actually behave like a function; macros introduce their own mini-language/syntax*

That's the point. S-expressions are structure notation language. The semantics of code expressed as s-expression is something else. There's the "default" one (as provided by #'eval), but macros allow you to work on the s-expressions as data structures. Ultimately, the macro expansion is still evaluated normally, but the expansion might be wildly different from the macro invocation.

This is a feature, not a bug. It gives you the power to add new abstractions to the language, as if they were part of that language in the first place. It's not something you need often, but there are things you can't do any other way. For example, since we're talking JavaScript - think of JSX. In JavaScript, it's a mind-bending innovation, though committing you to use a big code generation tool (Babel). In Lisp, you can do half of it with a macro[0].

A common use of macros is removing conceptual repetition in code. Imagine you have a concept in your codebase - say, a plugin. Creating a plugin involves defining a class extending a common base class, defining a bunch of methods that are identical in 90% of the cases, and a bunch of free functions. Conceptually, that whole ensemble is "a plugin". Lisp macros let you define that concept in code, and reduce your plugin definitions to just:

  (define-plugin some-name
    :some-specific-method (lambda () ...))
> In the last example you provided, I bet the macro implementation would look like a little interpreter?

Such macros are more like compilers. Interpreters execute the code they read; compilers - like those macro - emit different code instead.

> might be able to achieve the same behavior, right?

Yes, except the macro does that at expansion time - i.e. ahead of execution. In practice, this is almost always "compilation time".

> Just most of the time, I find functions are sufficient enough.

Because they are! Even in Lisp, macros are not your default tool for solving problems. Functions are. Macros come out when the best way to do something involves code that writes code for you.

--

[0] - And the other half with a reader macro. Regular macros transform trees before they're evaluated. Reader macros alter the way text is deserialized into trees. Reader macros are very rarely used, because they're a bit hard to keep contained and tend to screw with editors, but if you really want to create a different syntax for your code, they're there for you.

Re: The Problem with Macros

#59

"So we’re supposed to be writing a game, right? But in order to make progress, we have to fix a bug. And in order to fix the bug, we have to write a test. And in order to write a test, we have to write a test framework. And in order to write a test framework, we have to understand a thing or two about macros." You could just fix the bug? Game development is a different architecture to 'regular' system development (es…

> If you are testing to ensure the bug isn't reintroduced then you haven't fixed the bug. It’s called regression testing, and it’s a (fairly) common thing.

I know what it is - I don't think its relevant in this project

Re: The Problem with Macros

#60
post #7

Bravo, my friend, bravo. The unit testing macros for my Racket project were created so I could create snapshot tests for an API. The custom test-suite and test-case macros set parameters (think React context properties) for the suite and case names. There is a custom `check-snapshot` function which save snapshot files under ./snapshots/suites/{suite-name}/cases/{case-name}. It evaluates each expression and pretty-pri…

Talking about Racket and macrology, I think OP should get in touch with the PLT/Racket team. Matthew Flatt made many talks and papers about macro layers and proper scoping.

Parent comment is likely referring to the wonderfully named "Advanced Macrology and the Implementation of Typed Scheme" paper [1]

If you type macrology into Google you get the definition "Long and tedious talk without much substance; superfluity of words. noun." Perhaps accurate, but too vague ;-)

[1]: https://www2.ccs.neu.edu/racket/pubs/scheme2007-ctf.pdf

Post reply on HN