7 ms·
Its not exactly equivalent, but having spent the last month debugging a nightmare of C++ template meta programming mixed with a lot of C macros... I think thin
by overgard 8y ago
Its not exactly equivalent, but having spent the last month debugging a nightmare of C++ template meta programming mixed with a lot of C macros... I think things like this can be nice if they’re applied tastefully but I wouldn’t trust 99% of programmers to be judicious with these things. As someone else said, it’s catnip to a certain type of coder. It also creates a really nasty implicit dependency — you can’t pull out individual components because they’re dependent on the DSL, and who knows what dependencies the DSL pulls in? I would actually argue that macros might be why Lisp didn’t become mainstream; it creates a lot of fragmentation. Most people don’t even want to learn the syntax of their build system, asking them to learn some weird language extensions they can’t take to their next job seems like a rough sell
Edit: one more thing I forgot to add: this is a nightmare for tooling. I know there’s the contingent that wonders why you’d ever want anything more than vim or emacs, but for the rest of us, this just means refactoring/code navigation/syntax highlighting/autocomplete/static code analysis are totally broken.
- tincholio 8y ago>but I wouldn’t trust 99% of programmers to be judicious with these things The evil elitist in me wants to qualify that as "99.99999999% of JS programmers".
- deleted 8y ago[deleted]
- mitchty 8y agoYou've obviously not met many C programmers. I have a new general rule: if it confuses clang-format, it probably shouldn't be a macro in the first place. The amount of time I hit that pulled out of my butt rule in daily code reviews is above and beyond what you would expect. Also what attribute of JS programmers would preclude them to abusing macros or templates more than c or c++ programmers already do?
- sdegutis 8y agoLanguages, macros, libraries, and apps are in descending order of how many people will use them and how tightly coupled they are to their problem domain. Thus they should be designed and created in descending order of experience and domain expertise. Very few people should create languages, a few more macros, etc.
- chriswarbo 8y agoI agree w.r.t. languages -> libraries -> apps, but I would say that macros are part of a library, along with functions, methods, datatypes, classes, interfaces, etc. A typical library should usually define more functions/methods than datatypes/classes, more datatypes/classes than interfaces and more functions/methods than macros. The exception is libraries which only provide some particular macros or interfaces or whatever.
- sdegutis 8y agoMacros are a kind of function that have the ability to hide a lot of things, including control flow, and often do, which often makes them unable to be composed. That's why I put them between languages and libs. They're a little less flexible than regular functions/methods/classes and they usually have tighter lock-in.
- chriswarbo 8y ago> ability to hide a lot of things, including control flow, and often do They're still local transformations though, so I would think they're either: - Choosing between their arguments somehow e.g. like `(unless condition body)` expanding to `(if (not condition) body)`, or `(with-foo body)` running `body` inside some setup/teardown boilerplate. In such cases we just need local reasoning (possibly after expanding the macros; but like functions, if you have to keep reading the definition to see what it's for, the abstraction it provides has been lost) - Invoking other code (macros and functions); but functions can do this too, so such non-local reasoning is essentially the same. A phrase like "hiding control flow" sounds scary, like we're implementing "GOTO" all over the place (or `call/cc` or something). Yet macros themselves can't do that; they could only do GOTO-like-stuff if there are GOTO-like constructs in the language, in which case (a) that's what's causing the underlying difficulty and (b) functions can presumably do similar shenanigans. I must admit that I do most programming in Haskell these days, so I take it for granted that I can implement my own control flow using ordinary functions (thanks to laziness). That's just as well, since Haskell's macros (Template Haskell) are pretty gnarly; they're very useful on occasion though (e.g. I'm currently working on a script which reads in and parses a bunch of JSON from a filename (taken from an environment variable) at compile time, building a datastructure that I can query at runtime). > which often makes them unable to be composed Composed in what sense? I get that we can't e.g. pass macros to higher-order functions, and things like that; but I wouldn't say that's especially due to their use for control flow. I'm more frustrated by the general asymmetry between functions and macros, which can cause us to write the same functionality multiple times (I have the same frustration in languages which distinguish between functions and operators, functions and methods, statements and expressions, objects and "primitives", etc.).
- BigJono 8y agoIt seems quite intuitive that the complexity of code scales with the amount of build time logic it contains, whether that be C macros, JS transpilation, or whatever else. I think if a lot of developers programmed with this mindset we'd be in a much better place. The best example I can think of off the top of my head is people choosing Vue over React "for it's simplicity". You're really just taking the little bits of ritual and reliance on JS fundamentals that React contains (which isn't that much in the first place) and moving that complexity into a DSL with a big transpilation footprint. It makes sense from a "I want to play around and build features quick" perspective, but it's a horrid decision in terms of the complexity of the software and the number of moving parts it contains. Unless like many you (foolishly) believe the law of leaky abstractions doesn't apply in JS land for whatever reason.
- overgard 8y agoI couldn’t agree more. I almost want to call it the iceberg metric. If 90% of your complexity is hidden underwater it’s still there, you just don’t notice it until your boat crashes into it.
- TeMPOraL 8y agoBut this is also how civilization is built. We need that 90% to be underwater just to build the remaining 10%. Proper macros make that complexity much more accessible. That is, if things work correctly, you still don't notice, but if they don't, you get to easily fix them (though you still have to pay the cost of understanding it first). Macros can be seen as incidental tooling reduced to only essential complexity. For comparison, a typical JS transpilation toolchain seems more like 80% of incidental complexity.
- BigJono 8y agoI definitely agree with this. I think there's obviously a point where the complexity savings from using a macro outweigh the inherent disadvantages. But I'd also guess that a large percentage of the programmers using them wouldn't know where to draw that line, and probably don't spare a single thought on it. My point was more around instances where the supposed benefits of moving that logic to build time are superficial, which as you alluded to, is rife within the JS ecosystem. I hate to pick on Vue too much, because for the most part it's a pretty good tool, but in their docs they claim one of the motivations for building Vue and using it over React was that it's more familiar to people who have built traditional web apps, because it leverages more html/css knowledge rather than JS knowledge (it doesn't, other than in superficial ways, by the way. But that's a discussion for another day). This seems insane to me. Just as insane as if you used a ton of C or Lisp macros to make the language look like Python, just because your devs were used to Python. I feel like in those communities you'd get called out for adding a bunch of accidental complexity and told to suck it up and just learn C. But in JS land it's just par for the course.
- nicoburns 8y agoC/C++ macros are particularly horrible as they are essentially text replacement on the source code. Other languages (such as Rust) have macro systems which work with ASTs and can used in isolation without interfering with other stuff. For example there's a library in Rust which provides a `json` macro with syntax like so: json!({ "foo": "bar", "qux": baz }); where `baz` can be a Rust string or integer. Note: this will typecheck, so if you provide something weird as `baz` then it won't compile, but otherwise the syntax is identical to standard JSON syantax.
- ujal 8y agoWhats the canonical way to deduce the types in Rust from JSON? JSON Schemas?
- pohl 8y agoThis particular crate, serde_json, lets you create any type that implements Serde's Deserialize trait when you're parsing.
- steveklabnik 8y ago... and it also provides a dynamic interface too, if you need to work that way. You probably don’t...
- xfer 8y agoThat still doesn't address the issue of macro syntax being foreign/not easy to refactor. See the nom library for an example.
- monocasa 8y agoRust procedural macros are just regular rust that runs at compile time. You get vector of tokens in, and return a new vector of tokens out.
- 8y ago
- Fellshard 8y agoExactly this. Given how a majority of JS devs tend to not learn from history and other languages, I would trust them with macros about as much as I'd trust a baby with a sportscar.
- rauhl 8y ago‘Most programmers can’t be trusted with macros’ is a sentiment I read a lot, but at one point in time people might have said the same about functions, and arguably they could say the same about pointers or recursion today. I think that being able to think symbolically about one’s code is a vital skill for a programmer (and, more generally, that the capability of thinking abstractly is a vital skill for a learnéd human being). Thus, if someone lacks that skill, perhaps he should probably not be a programmer? That may sound revolutionary, or elitist, but I think most of us would agree that someone who can’t effectively reason about a depth-first tree traversal will have trouble as a professional software developer; mayn’t that also be the same of someone who can’t effectively reason about computational structure? Your comment re. macros & fragmentation makes me wonder if you’ve ever used Lisp’s macros: I think that they actually mitigate against fragmentation, as e.g. CLOS started out as macros atop Lisp and ended up being standardised as part of it. Lisp’s packages help manage namespace collisions, and thus prevent fragmentation by making differences manageable. And Lisp tooling typically handles macros very well indeed.
- beaconstudios 8y agothe unique problem with macros (at least in the C/C++ world) is that they can be used to invent new syntax. Now when you join the company, not only do you have to learn the code, but you also have to learn the language extensions at the same time. If there's a lot of them, you're effectively learning a whole new language with likely minimal intentional design, with all the potential problems that a new language comes with (unintentional/hard-to-understand side effects, performance issues in the output, poor domain fit, etc).
- shados 8y agoFor all the flack Java gets, it got something right on this topic imo. Take the lowest common denominator of language features (for a time), and have the solution to almost everything be code written with those very, very few constructs. Yeah, it ends up verbose as hell, but even though I haven't done Java since the early 2000s, I can look at our backend code (which is all in modern Java), and short of a few new things like annotations and lambdas, I can read it just fine. The problem space is very, VERY well understood. The patterns have been bikeshedded to death almost 20 years ago. It's super boring, there's not many interesting left, and it's awesome because of it. Of course, software engineers hate this in general because they get way too much enjoyment out of figuring out how to save 2 lines of code for their own good ;)
- eadmund 8y ago> I know there’s the contingent that wonders why you’d ever want anything more than vim or emacs … Well, we have tools that work. Why would we use tools that don’t work? It just seems weird to me, when a solution to a problem exists, to not use it.
- tomjen3 8y agoI am not so sure about the tooling situation, at least with Lisp macros you can run the macros and work on the underlying, generated code.
- zamalek 8y ago> this is a nightmare for tooling I self-inflicted some pretty awful debugging woes due to C++ macros a few years back. VC++ is incapable of stepping into/through macros - so a logical error in my macro had to be solved with good-old printf debugging. I learned my lesson about using macros sparingly. This can obviously be alleviated with source maps. This presents a new set of dilemmas: does the developer want to debug the macro in its macro form, or its expanded form? Does the developer only want to debug a specific macro in expanded form, while keeping other macros expanded? How do you even debug a "case macro" in macro form? How do you debug the case macro code while it is expanding?
- flavio81 8y ago>template meta programming mixed with a lot of C macros... The author cites Lisp macros. C macros, or C++ template metaprogramming, are to Lisp Macros as a 19th century locomotive is to a state-of-the-art MagLev train. >I would actually argue that macros might be why Lisp didn’t become mainstream; it creates a lot of fragmentation. This has no substance. Please take a look at how macros are used in Lisp. >this is a nightmare for tooling Not in Lisp.
- overgard 8y agoYou say “in lisp”. But which Lisp? Common Lisp or Scheme? And then which implementation of scheme? And which object system? See, that’s kinda my point. If I talk about Python or Java or C# we’re not even having this discussion, because they’re monolithic languages and features are core to the language, not built on top. On the other hand I can’t really move my code between Lisps. And I don’t buy the argument that it’s just because Lisp has been around a long time. Python is almost 30 years old but there’s one canonical version. And seriously, I bet there are a lot of pythonistas that would change things about, for example, the object system if they could (explicit “self” comes to mind), but if they did that the community would fragment with some people using the original system and others using an alternative and it would be hard to integrate new libraries and know the rules of the module you’re working in. It’s a trade off no doubt, but it seems like most people have voted that a rich ecosystem of libraries without compatibility issues is more important to them.
- lispm 8y agoThat's why Lisp has a main standard. Common Lisp. Most other languages, derived from Lisp, are incompatible - like Ruby is incompatible with Perl. Thus Scheme is called Scheme and not Lexical Liwp - it's now a new language family - the family of Scheme dialects - which are grounded in RXRS reports as language standard. It's good that there is only one/two Pythons - but Python competes with a zillion other scripting languages - or they compete with Python. And they are not compatible at all.
- overgard 8y ago