So I'd argue for this differently: defn and for and other pervasive macros aren't safe because they're well-tested, they're safe because they're trivial. You know that (defn ...) is short for (def (fn ...)). You know exactly the code that that macro expands to. And you can choose to type (def (fn ...)), or you can choose to type (defn ...). Same with short-circuiting "and", or "for", or "+=", or whatever.
That's probably true of a lot of macros, but I would be surprised if the macros included in Lisp are all of that triviality.
And the biggest perceived payoff of macros developed in user-space is going to be the macros that aren't trivial.
This is a very good point. Destructuring assignment, maybe? Arrow functions? I think we agree that some language features are good additions, even if I picked a bad example :)
Sure, I see your bigger point, and arrow functions are a good example of it. I mean, obviously you can do `function(foo) { ...; return bar; }` and then use bind() to fix the fact that that doesn't close around the right things at all, but arrow functions are just so much clearly less error prone that there's no real argument they're an improvement.
I'm actually not aware of destructuring assignment being available in JS, but it's great from Erlang, so I'll have to look into that.
This is something I think about a lot because I'm writing a compiler/interpreter for my own programming language: programming languages often introduce too many features before they're really thought out well enough--meanwhile lots of those features would be just fine as libraries. The result is an inconsistent language with lots of ways to do the same thing that don't play well together. C++ has this problem so badly that they've developed toolsets for subsetting--i.e. choosing the set of features of the language you use and returning errors if you use features outside that set. And then there's stuff like C which just solves that problem by rarely adding features.
The most powerful languages, I think, tend to be ones that didn't add a ton of features, and made good choices on the features they did add. For one example, I think Erlang got their threading features really, really right, and a lot of other programming languages are going to regret going with other threading models and bolting on an Erlang-style model later.
I don't know when the long run starts, but my experience with macros is that they're just a huge productivity benefit that I wouldn't want to give up. I haven't seen a codebase degrade into an idiosyncratic mess -- perhaps, in part, because there's a lot more friction to writing macros in OCaml than in Clojure, so macros are only used where they provide a substantial and obvious benefit. Or perhaps it's that typechecking makes it much harder to write poorly-behaved macros. Or perhaps I just got lucky with my cubicle assignment :)
I dunno! I haven't written much OCaml, though I've written some F# which is supposed to be pretty similar. I do think making powerful-but-dangerous features harder to use can be a good strategy for dissuading their use except when they're really needed, so you might be on to something there.
Comments
That's probably true of a lot of macros, but I would be surprised if the macros included in Lisp are all of that triviality.
And the biggest perceived payoff of macros developed in user-space is going to be the macros that aren't trivial.
Sure, I see your bigger point, and arrow functions are a good example of it. I mean, obviously you can do `function(foo) { ...; return bar; }` and then use bind() to fix the fact that that doesn't close around the right things at all, but arrow functions are just so much clearly less error prone that there's no real argument they're an improvement.
I'm actually not aware of destructuring assignment being available in JS, but it's great from Erlang, so I'll have to look into that.
This is something I think about a lot because I'm writing a compiler/interpreter for my own programming language: programming languages often introduce too many features before they're really thought out well enough--meanwhile lots of those features would be just fine as libraries. The result is an inconsistent language with lots of ways to do the same thing that don't play well together. C++ has this problem so badly that they've developed toolsets for subsetting--i.e. choosing the set of features of the language you use and returning errors if you use features outside that set. And then there's stuff like C which just solves that problem by rarely adding features.
The most powerful languages, I think, tend to be ones that didn't add a ton of features, and made good choices on the features they did add. For one example, I think Erlang got their threading features really, really right, and a lot of other programming languages are going to regret going with other threading models and bolting on an Erlang-style model later.
I dunno! I haven't written much OCaml, though I've written some F# which is supposed to be pretty similar. I do think making powerful-but-dangerous features harder to use can be a good strategy for dissuading their use except when they're really needed, so you might be on to something there.