Is sigil_H an example of a macro that is unhygienic?

After reading Metaprogramming Elixir, and the hexdocs on metaprogramming and macros, I was wondering how this ties into macros we use everyday.

The examples about hygiene of macros show how you can use var!/2 to explicitly reach out to the variables declared in the caller site and alter them. Doing so is considered unhygienic. It’s not forbidden but it can confuse the user of the macro.

I’m wondering if just reading a variable in a macro with var1/2 is also considered unhygienic.

The sigil_H macro in LiveView does exactly this. it gives a runtime error if you try to use the sigil, without a variable assigns available (probably just for better developer experience, to avoid a more generic error if the variable is not found). The assigns variable is like an extra input parameter for the macro (sigils only accept the string and a modifier so there is no other way to use sigils in this way).

I totally see how this is a good design decision. If used sparingly, this is the way to go. Just reflecting and wondering if I connected the dots correctly here.

1 Like

That’s a good question. As you said:

The examples about hygiene of macros show how you can use var!/2 to explicitly reach out to the variables declared in the caller site and alter them.

Emphasis mine. Is it unhygienic if it doesn’t alter variables, only reads them?


Interesting. I also blew passed the “alter” part and always thought of ~H as unhygenic since you must have a seemingly “magic” binding to exist for it to work! And yet, once in a while I write some helper macros that must be used in a specific context to work and I never think of them as unhygenic… huh. How 'bout that!

1 Like

You could still argue that a macro that reads a variable with var!/2 is unhygienic because it makes an assumption about how it’s being called, in an inexplicit way. The documentation of sigil_H for example doesn’t mention this requirement explicitly (although it doesn’t surprise too many people, as there are plenty of examples, and the error shown when the assigns variable isn’t in scope is very informative).

Being ‘hygienic’ vs ‘unhygienic’ is just a label. Its intend, as I understand it, is to encourage developers to choose the hygienic path over an unhygienic one, if both are feasible. There are no technical consequences.

I’d summarise like this:

hygienic (not making assumptions or altering the caller context) > reading with var!/2 > altering with var!/2

Perhaps “read unhygienic” and “write unhygienic”?

:slight_smile: as a concept I like it!

I’m not sure if the distinction helps developers stay on the “good side” (if at all possible). But I understand the nuance with these terms.

I’m curious if other languages with similar metaprogramming capabilities make this distinction.

To me the excuse heex has to depend on assigns unhygienically is that it‘s a sigil and therefore cannot take parameters. Sigils were already well established for templating, so it also didn‘t make sense to switch away from using a sigil.

1 Like

I believe the biggest issues with unhygienic macros are “implicitness” (when we implicit read assigns) and “overriding” (when we accidentally override a user variable or alias). Being “read unhygienic” means that at least there is no chance of accidentally overriding a user variable behind the scenes.

After having let it simmer for a while, I think the biggest reason why read unhygienic is less of a problem, is because it will raise an error at compile-time, when the context of the macro doesn’t provide the implicitly read variable (is this always the case? I might be wrong here). So the implicitness becomes explicit, when you try to compile such code :slight_smile: It never gets to be compiled, luckily.

Whereas write unhygienic code can go unnoticed, which is a big problem, if you weren’t expecting that.

So this is what I get to:

hygienic > read hygienic (won’t compile if expectation about variable is not met) > write hygienic (can go unnoticed)

My understanding of macro hygiene is becoming more solid from this thought exercise, so thanks!