I'm not saying you're wrong, exactly, because there are qualitative differences. Manipulating syntax is definitely more powerful, and enables some truly crazy and wonderful constructs. It should be used responsibly.
But when we define an API, we're inventing a new system of primitives for the problem at hand. It's really not as conceptually dissimilar from a language as we'd like to think.
You might argue that the distinction between APIs and language features is arbitrary. But I'd say there is a meaningful difference. An API helps you tackle a particular problem domain, such as making an HTTP request, communicating over a USB port, or writing an image. Whereas a language feature is much more generic. It's not limited to any particular problem domain. Rather, it cuts to core of how we express problems in general.
If we're all ostensibly writing JavaScript, I don't think we should be using multiple, incompatible variants of the same language features.
There are a lot of ripe ES6 features to implement as macros. That `class` macro is actually taken from ES6 and has been standardized for JavaScript, so it's not exactly random.
I'm sure everyone who has ever looked at C macros has thought the same thing. It's a great theory.
In practice, having macros will lead to someone abusing them and eventually, that abuse will be become institutionalized.
Don't say "it could never happen here" because it can and it inevitably will.
I don't have a specific problem with all of this, except I keep thinking a better solution would be to hammer browser manufacturers to keep their shit up to date or explain why they are not doing so.
I'm willing to run into the occasional macro abuse to get goodies like core.async and core.typed.
That's the thing though. When a new proposal comes out, if it's released in the form of a macro it can be tested without vendor buy in. The pressure relief on vendors to keep up to date with the standard is just an unintended (maybe negative * ) side effect.
* Personally I like being able to count on "current" features and I'm not opposed to using shims to patch the environment up to a workable level.
(Forgive the irresistible pun, but the sentiment is sincere: This feature is based on Scheme, so looking at C seems misguided.)
On the other hand, there are merits in their differences. Chrome, although it would prefer Dart, looks upon JS as legacy that they can approach via a good VM/JIT, and a brilliant Developer Tools suite.
FF on the other hand is leading into the JS futures on two fronts: asm.js for both performance and C/C++/.. integration into the browser, and ESnext. Google is very reluctant to invest in ESnext now, just look at the compatibility charts.
This makes sweet.js strategic, it lets us at least experiment with ESnext features as far as macros can take us. I'm hoping sweet.js can also help us make use of asm.js from within the browser buy building macros for things like structs, which halve or more memory footprint of object arrays.
Lets be aware of the war, and do what we can do to resolve it.
import { foo, bar } from "macros";
The scoping of macros will all stay in tact; any other macros from "macros.js" will be available, but the code that `foo` or `bar` expands to will be able to access them (this is really nice for helper macros and other things).
So yes, modules are going to be a big part of distributing and composing macros.
Scheme and other Lisps macros to build new syntax on top of a small core language.
Are you tired of typing (function() { ... })() and other such forms? If so, you will like macros.
Here is the full list:
On the building side we have Grunt and the less popular but still useful Jake.
Ember's property system is a DSL.
Angular's DI syntax is a DSL and as much as I love Angular there's really nothing to like about the horrible mix of ([{ that one has to use here.
Actually Ember and Angular and just about every meaningful javascript framework defines several small DSLs.
Pencil Code is an excellent learn-to-program DSL built on Iced Coffeescript.
Coffeescript's crazy syntax makes DSLs slightly more approachable than Javascript but only for a while.
Express and most server frameworks define a DSL.
Anytime you find yourself passing a non trivial config struct you're really playing with a DSL.
This is by no means a complete list.
Using DSL to describe your domain (application) is normal. One application = one domain.
Learning in addition 15 different DSLs for libraries and tools, just because it's cute, is not the way to go. Soon you will argue about the exact definition of monad instead of shipping code.
Macros are used to great effect in Lisp land. They can be used to great effect in JavaScript land, too. With great power comes great responsibility.
Does anyone know Arc well enough to point out some macros used in HN's source?
No it really cannot, except if it's exported there.
Urgh, I just got a sinking feeling in my stomach because I know how right you are.
As long as you don't have different definitions in the same JS file, it's not really a problem because each file can be compiled down to JS independently of the others. I'd imagine you could even add a package.json flag to specify which version of a sweet.js "syntactic sugar" library to use for a given package, and npm could compile the files automatically on install.
The more transparent the sweet.js workflow can be (much like LESS/SASS is for CSS), the more likely it is to be adopted.
But it'd be a complete mess of a code base. Code is read much more often than written, easily understanding a code base is incredibly important, and really hard in larger systems.
If even your syntactical constructs behave subtly different (let alone the code you're writing!), you're setting yourself up for unpleasant surprises.
There's an excellent book on UX, "Don't make me think". The same applies to code: strive to reduce the cognitive load it takes to understand a piece of code as much as possible.
Agreed. And it seems macros can really help with that, by encouraging a DRY codebase.
Syntax to help with common tasks is nice, but if you end up with lots of ad hoc syntax for lots of very divergent, and maybe not so common tasks, you're in trouble. The problem is just the multitude of syntax constructs that typically end up way underspecified, as opposed to the much more formal and strict models of a language that went through a specification process. Imagine learning not three kinds of loops (for, while, do), but 300 subtly different ones in one code base. Ugh.
On the other hand, lots of boilerplate make code unreadable just by the sheer amount of it. This is a very hard balance to strike, I'd rather err on not giving every developer on a code base the power to add new syntactical abstractions.
In most lisp dialects, macros are lexically scoped.
As long as sweetJS follows lexical scoping, a set of macros could be contained within a self-executing lambda like most libraries already are.
Why? Because learning languages gets easier every time you do it. And it opens your eyes to languages' internal structure, the way languages are composed, which makes using given language effectively easier.
So now you know 5 or 8 languages and you discovered a few basic principles behind most of them and you understand how semantic constructs compose and you know about various syntactic representation of any given semantic statement (and the other way around, similar syntactic constructs having different meanings). And you come upon a macro, like the one in the example.
You don't know what it's doing - well, you just check the docs and then you know. It's as easy as learning what a function does, really. So what was learning those other languages for? It was just to reduce your fear of introducing new syntax for things. You won't think "oh, how dangerous this could be" but rather you just start using it, because you do it (change syntactical constructs you use) all the time when you switch between languages.
At this stage you can learn some Lisp. And write some real macros. And maybe try Nimrod, which has wonderful macros too. And maybe Rust, which I didn't try but I read good things about. After this you'll understand what macros are, how they are constructed and how they compose - and write many of your own - and then you look at the class example above and you instinctively[2] know that there has to be a syntax transformer which matches class keyword, a keyword and a block, and then there has to be another transformer, perhaps recursive (like in Scheme) or just iterating over the body (like in defmacro) and that all they do is to add some keywords here and there and a statement or two at the top of the block. In short - you know exactly what to look for and even docs are unneeded, you just take a quick look at a macro source and go on using it happily. Alternatively you can look at generated code and work out macro implementation from it easily, too.
I'm a happy user of LiveScript, and CoffeeScript before that. The only reason for not switching to JS with sweet.js is the fact that I would need to write a whole host of macros by myself, while in CS and especially LS they are already written. And of course modifying their grammar is not that hard either. Had I somehow been unable to use LiveScript, I'd use sweet.js for sure. What I'd implement? Probably some kind of "let" statement for sane scope management, a loop macro for iterating over custom collection classes (loop in CL, for/* in Racket, also in others), function currying (partial application) and maybe something like "with" context managers from Python would be among the first. There are many things which are better abstracted with syntax than with (for example) functions.
Would the language be JavaScript? Well, hard question, it would be a superset with underlying semantics intact, but it sure wouldn't "feel" like JS. But would that matter? Not at all - syntactic extensions would save me a ton of time and I'm used to many different syntaxes anyway. And I expect anyone who'd like to work with me to be able to pick up any language in reasonable time - the ability to pick up a few additional syntactic constructs on top of already known language is essentially the same thing, so I think such person would have no problems with it either.
Anyway, macros are good; using them is good; the only dangerous thing is having many people implement essentially the same macro over and over again, but I think this could be solved with a sane module system for macros which I read is already planned. There should be more languages rather than less; more exploration of different syntaxes and semantics for things, not less - and macros are one way of making this happen. And if you have problems with learning what a "class name body" does, then I can only refer you to the first paragraph.
[1] After I wrote this post I realized I could sound a bit rude. It was not my intention and I'm sorry if I offended anyone; while written generally, it's actually only a description of a road I personally traveled, so obviously YMMV.
[2] I know completely nothing about sweet.js in particular, so I'm completely just guessing.
The reason is because I've come to understand that social factors trump technical factors nearly every time. The hardest part of writing a software system is almost never solving engineering challenges or writing code, it's communicating the solution to everyone who will have to touch it. Over a software system's lifetime, you will have to read code much more often than you will have to write code. Macros make writing code really easy and communicating everything about what it does, how it does it, and what invariants need to hold for it really hard. That's usually a poor trade-off for anything non-trivial.
The reason I think DSLs are good but macros are bad is because DSLs make you think long and hard about the costs of implementing a new language. You have to pay for it up-front by writing a compiler and hooking it into your build system, even if you have help from the host language's libraries, and then you'll end up with something that's clearly a separate language and must be documented and learned separately. That cost makes it clear that creating a DSL is not something to do lightly, and you need a problem domain where there really are much higher-level abstractions that can't be captured by an existing programming language.
The problem with macros is that you can create a new language construct with a half dozen or so lines of code, use it in a few hundred places in the codebase, and now your maintainers have a few hundred problems.
Is it just that, when a maintainer sees a function call it's easier to guess what it does, then when they see the use of a macro?
This does not refute your point at all -- that macros can be very helpful when implementing DSLs -- just wanted to point out that macros are not a requirement for doing so.
Also, a DSL expressed using first-class entities will fit into and integrate with its host language more nicely.
Unless your problem doesn't absolutely need them. It's considered bad style to use them when not absolutely necessary because they are much harder to reason about.
Macros can be dangerous in the 'give a man a hammer, everything looks like a nail' way. Like when some Python people went mental over meta classes using them everywhere they weren't needed.
I love macros (in lisp) but you shouldn't need to use them that much. They aren't always good.
Isn't this true for almost everything? For example a while back there was this talk "Stop writing classes", where someone argued that you should just use functions for many simple cases (with nice examples etc).
Generally you should abstract on the level most suitable to the problem at hand. Some problems are best (for some value of "best") abstracted with a global var, others with a set of functions, others yet with hierarchy of classes. Syntactic abstraction is just another tool in your toolbox and it is very handy sometimes; of course it's not fit for every problem. But no one is going to do a "Stop writing macros" talk anytime soon, just because no one is writing macros - most languages lack them completely or have only silly string-oriented preprocessors, and even in languages that support them macros are feared and avoided.
I would just like to see macros go "mainstream" - if this means they'd be abused a bit (like classes/OOP now, for example) then I think I can live with it. Probably - it's quite possible I'd come to regret it very quickly :)
You know nothing about sync, I presume, yet you assume he/she only knows very few languages because you don't agree. Don't do that.
I don't agree with sync, just pointing out that "go home and learn some programming first" is not a great argument.
please correct me if i'm misunderstanding.
Not at all - I think it's ok to invent languages, period. Little or huge, DSL or general, using established syntax or creating new ones - making them is good. The results can be bad (and frequently are), but that's to be expected of every process.
Knowing different languages just helps in seeing syntax for what it is: another level of abstraction, not a set of rules cast in stone. You can come at the same conclusion with a proper university class or with any number of other means - I mentioned learning languages just because that's what worked for me.
It's true that different compilers and interpreters sometimes do slightly different things, but atleast there's a standard which should improve over time. I don't see that happening with macros.
Not sure how modularity is achieved with macros either.
So yeah, "correctness of implementation" is something to worry about, you just haven't had to (yet).
I will not be doing metaprogramming again any time soon.
I'm doing just fine using composition/iteration and creating better, more fluent APIs.
In the example you cited, it seems like a pretty good language versus other examples I have seen for defining Javascript types?
So there will be newbies who make messes with macros. It's happened in the Lisp world for decades -- though not as often, probably, as some people fear -- and it will surely happen here. Some people will overreact to that (as they have to misapplication of operator overloading) and prohibit their teams from using them. Eventually, knowledge of how to use them well will spread wide enough that most people will be able to be trusted using a language that has them, even if that means, in many cases, that they know they don't have the experience to use them properly and so avoid writing their own.
In this vein I'll close with a quote from the great Lisp hacker David Moon: Functions compute; macros translate.
sweet.js is more or less a Lisp macro system for Javascript. Gaining some familiarity with Lisp macros would probably serve you well in terms of being better able to understand what's on offer here and how useful it can potentially be.
You really have no idea what this is doing (the canonical example from sweetjs.org):
It's not javascript -- it's your own made up "language" of macros.I do believe with a steady hand this could lead to some great things, but the example on sweetjs.org seems rather heavy handed. Having different `class` macro definitions between codebases that use sweet.js will devolve into madness.