36 comments

[ 3.5 ms ] story [ 79.7 ms ] thread
this is already covered in basically any AST transform systems (babylon, acorn, TS...)
This predates those, and is hygienic.
I believe that Babel lacks custom infix operators whilst Sweet.js provides them.
You're absolutely right; Babel's parser is very much hand-coded, and adding syntax requires deep knowledge of the internals. Not impossible, but not for the faint of heart. My dreams of having the safe navigation operator from Coffeescript (a?.b?.c) must yet remain unfulfilled...

https://github.com/babel/babylon/blob/master/src/parser/expr...

Well "a.b.c" violates the principle that you should never access a property more than one level deep anyway ;)

But in this case it's pretty straightforward to write a function like "get('b', 'c')(a)" or use a lib like lodash [1]

[1]: https://lodash.com/docs/#get

The Law of Demeter doesn't always apply neatly, especially when you're introspecting JSON sent over the wire and you don't have a "class" with object oriented behavior for each sub object (at best, they're strongly typed). And the runtime overhead AND verbosity of a function may not be ideal, with the string parsing required in lodash and the awkward syntax of the curried function. Something that compiles natively in a DRY way to a && a.b ... would be used widely imo.
That raises a question: What's stopping Babel from using Sweet.js instead of a custom parser?

Are there any transpilers that do use Sweet.js?

Contracts.coffee IIRC, uses Sweet for Contracts.
That's not just "AST transform system", Sweet.js provides a way to extend grammar with new productions. That allows to introduce new syntax constructs.
Coming from a Scheme background, if there's one thing I can't believe we live without in Javascript, it's macros. Hell even C has macros... I've looked at Sweet.js a few times to add support for macros to the Linode Manager. Between m4, the gcc preprocessor, and something native, this seemed like the easiest potential sell. However, I've been turned off by how complicated this looks compared to any of the other options and I'm not sure it actually even supports the feature I want: allow some code to show up in "development" mode but for that code to not even exist in the compiled output of "production" mode. (The other feature that's just as important though is being able to wrap shared code up in a single function that gets compiled in at compile time, but I think Sweet.js supports that.)
Who uses m4? It has been around forever, but I have never really seen it used except in autoconf. When I looked at it (a long time ago) it struck me as really tricky and confusing, even more than lisp macros (but maybe on a par with TeX). However, this was just a first impression -- I didn't go too deep.
I have used it recently. I have a similar attitude towards m4 as shell scripts: they are good for quick hacks, proofs of concept, and very simple systems. When I need to sprinkle just a bit of macros on something, m4 is great. For example, a one-off piece of technical documentation I wrote is markdown interspersed with code examples. I wanted to make sure the code passed its unit tests, so it lives in separate files and I use m4 to include it into the bigger document. This is nicer than catting separate file chunks together because the text can all be contiguous, but if I needed to do anything more sophisticated I would probably switch to purpose-built templating or static site system.
I've used it for really simple templating stuff with Dockerfiles - which it works fine for.
I once learned a lot about m4; more than twenty years ago. I remember all the features it has, like redefining the delimiters for macros, diversions, for loops and all that jazz.

So it's not due to lack of familiarity that I won't use it; it is simply atrocious.

Recently I needed a preprocessor (featuring just simple if/ifdef/elif/endif) in a Linux distro build system (Yocto). I didn't want to add anything new.

To avoid inflicting m4 on myself and everyone who has to interact with this in the future, I wrote a simple awk script wrapped around cpp.

The tiny awk script implements this pipeline, which involves encoding and decoding the input so that cpp never sees the syntax of the file:

  this is our                1                         1             this is our
  original                   2                         2  --> [dec]  original
  @ifdef FOO    --[enc]-->   #ifdef FOO  --[cpp]-->    6             file
  input                      4
  @endif                     #endif
  file                       6
this works with nearly any textual input. Your input can contain broken C tokens and comments and whatnot.

I switched the # to @ to remind people that this isn't the C preprocessor, and because # is a comment character in the preprocessed language, which could look confusing.

Heh, I've used it with Docker to generate config files at build time using build arguments and environment variables. Worked surprisingly well!
Webpack can do that for you via dead code removal
Uglify has been doing it for years too - just do a quick env var string substitution and it’s trivial, Webpack or not.
The in-the-wild code I've seen that uses Sweet.js has been atrocious. Of course I'm not saying that this is necessarily the case. I'd love to see some real world examples whter this is used effectively.
Separately, could you point to some examples of the bad Sweet code you've seen? If it's anything like C macro misuse, it'll at the very least be educational to see just how badly one can shoot oneself in the foot.
I must say I don't see the use case... What real world examples do you have?
Also known as: 'how to build your own javascript dialect that you won't be able to hire for'
The last thing the JS community needs is macros IMO. From what I've seen in other languages, macros are difficult to maintain and document, and they add syntax constructs to languages that aren't supported by code highlighters. The difference between this and babel, is documentation. Babel is working off of a specification of how the language should work, whereas I doubt someone will document their macros as well as TC39 does the JS syntax
Every language benefits from macros, and certainly JS.
We were looking into sweetjs for adding cross-module contracts to the graphistry stack, but nixed the project when it became too much to generate code that passed our basic linters. TS largely scratches that itch, so we've been moving to that. I'm still hopeful though :)
What would hygienic mean in this context?
Not C macro-like source code manipulation as dummy string but as more smart structure like AST.
It's to do with how variables leak into scopes from (and to) macros, iirc
Can someone please explain macros, in an easy to grasp way?
Macros are a tool given to the user by PL designers to extend the language with new syntax.

I.e. a macro program is a piece of code that defines new keywords, operators, etc. and their implementation.

Say you're creating a programming language and you know everyone is going to love it and use it. You add some nice features like if statements, switch statements, and for loops which allow the people that use your language to decide when certain code runs and shortens the syntax for common patterns.

Five years down the line your language is losing popularity fast, because all the hip new languages have an "unless" statement which is the opposite of an if statement. They have for-each loops, cond statements, and their if/else statements return values, so you can write less code to do those common patterns in those new languages than your language.

You rewind time 5 years using a time machine and this time, when you create your langauge, you add all those cool features, only to realize in another 10 years your language has lost popularity because there's a new set features they want, lambdas, argument piping, etc.

So this time when you go back in time, you add macros to your language. Macros are bits of code that allow people using your language to write code that produces more code. Now, you don't even have to write an unless statement, you can let the community write one for you and release it as a package. Anyone at any time can effectively add features to the language in their own project.

That sounds fantastic, and it often is, but macros need to not be abused. If a developer writes a bunch of code using lots of his own macros in your language, anyone reading that code later is going to be very confused seeing that many language features that aren't in the language docs, because that random developer wrote them himself. Functions are much more explicit than macros, so if you can write whatever you're trying to write as a function instead of a macro, you'll be better off. For every success story of a language becoming easier to use because of macros, there's another story of a different language becoming frighteningly complicated due to macro abuse.

A "macro", in the sense we are using (i.e. not "C preprocessor macro" or "Excel macro"), is basically a function or procedure that "spits out" (returns) source code. It is writen in the programming language you use, and outputs source in the programming language you use.

In other words, a macro is a program that writes programs.

In languages that make heavy use of macros (like Lisp), a macro is compiled almost transparently, so you can use it as if it were a function. It behaves like a function but can do a lot more.

One simple use is to eliminate having to write repetitive or "boilerplate" code, since you can just program a macro to write generate code for you. This generation occurs on the fly and transparently, so the code is more maintainable as well.

Macros run some code at compile time, that takes raw unevaluated source code as arguments, and returns new source code to replace it in the code. Then use this returned code instead in the compilation.
Thanks for all your great answers! I've learnt something new.
I personally prefer MetaScript [1] as it's comment based so JavaScript highlighters/linters don't have issues with it.

It is also essentially JavaScript so no special syntax had to be learned.

[1] https://github.com/dcodeIO/MetaScript