9 ms·
And that's why, in my opinion, non capitalized macros are always evil (yes, I'm looking at you linux kernel). When I see a lowercase symbol used as a variable,
by simias 13y ago
And that's why, in my opinion, non capitalized macros are always evil (yes, I'm looking at you linux kernel).
When I see a lowercase symbol used as a variable, I expect it's going to be some actual global variable. When I see a lower case symbol used like a function I expect it's an actual function. And then comes the day I want to take the address of the "function" and it fails. Or worse, I make a syntax error somewhere and because of the macro expansion it gives me the most perplexing error message in the world and it takes me 5 minutes and digging through 4 levels of include files to realize I forgot a colon or something.
Not to mention the mess if the macro is poorly implemented and does things like using a parameter several times which causes hard to catch undefined behaviours in the application. Nothing worse than having a seemingly harmless code like "foo(a++)" turn into the bug of doom because you didn't know foo was a macro. Please don't do that.
On the other end when I get a weird behaviour/error message around a capitalized symbol I automatically think that it must be a macro and go dig for the definition.
- pcwalton 13y agoYup. This is why in Rust macros have to end with a ! (and syntax highlighters know about this). This was a hard call, because it prevents using macros to create forms that look exactly like the built-in ones, but it prevents issues such as the one described in the OP.
- ajross 13y agoBut... the ability to use macros to create forms that look like "real" syntax is sort of the point. I don't think you can fix this with a language feature or style convention. Metaprogramming is "dangerous" because the meta constructs don't (can't!) act like what they look liks. Metaprogramming is "great" because the meta constructs look like simple programming but have surprisingly deep behavior. As I see it all you did was add a "!" to the confusion, honestly. People likely to get tripped up by e.g. the "address of macro" problem above are going to be no less confused by the extra syntax IMHO.
- kibwen 13y agoWhen you see an exclamation point, you know that compile-time fishiness is happening. If you don't see it, you know that there's nothing fishy going on. You might not think that's useful, but I hardly see how it can add to the confusion. For clarity I just want to note here that Rust macros are structural (like Scheme) rather than textual (like C).
- pcwalton 13y agoPeople don't expect an ordinary-looking identifier like "linux" to macro-expand to something else, like the number 1. That was the source of the confusion in the OP. Syntactically distinguishing identifiers that will be macro-expanded from identifiers that won't does solve this problem.
- ajross 13y agoBut again, that's situational. This particular macro is confusing because it (1) looks like an identifier and (2) doesn't act like one, instead being a 1970's style platform identifier that (3) no one actually uses in favor of constructs like __linux__ or __GNU_SOURCE__ or whatever. But the more abstract point is that macros allow you to provide abstractions that do look just like an identifier (or function call, etc...) and do act like identifiers in the context where they're used. And that this is a good thing, because that kind of syntactic flexibility is useful in defining domain-specific languages that make the expression of hard problems clearer. Adding a bunch of "!" to those DSLs isn't helping anything. Basically, we have a macro that was abused here. Your solutions it to make it harder to abuse macros, mine is to accept the occasional abuse (with a "don't do that" in most cases, those here it's a backwards-compatibility problem) in exchange for more clarity on the hard stuff.
- andrewflnr 13y agoAs I understand it, it is one of Rust's goals to be predictable in the code it runs/emits. This is fitting in a language designed for performance-critical systems. I see what you mean about the point of macros being that they can transparently add compile-time behavior. That's just a different, inconsistent philosophy from that of Rust.
- simias 13y agoAh! That's great, I didn't know that. The more I hear about rust the more I like it. But then I'm reminded that it bakes garbage collection into the core language... Oh well, perfection is no of this world.
- pcwalton 13y agoGC is being removed from the core language and will migrate to a library before 1.0.
- adrusi 13y agoThere's a project, zero.rs, that lets you have no runtime in rust. It's been shown that it's possible to make a simple kernel with it without too much pain. The disadvantage, of course, is that libraries will use garbage collection, and as soon as it's used, you get the runtime, so you can only use certain libraries designed for zero.rs, which causes fragmentation. All this would happen if gc was in the std library anyway though.
- pcwalton 13y agoRust libraries rarely use GC. The standard library goes out of its way to avoid it wherever possible, while remaining safe.
- simias 13y agoFair enough. I just have to cross my fingers and hope the GC won't catch on then! :)
- kibwen 13y agoWe strive to avoid GC like the plague in the stdlib, and the language itself encourages users to do the same. I wouldn't go so far as to say that we try to make it hard to use GC in Rust, but we definitely believe that it's currently too easy to resort to it over better alternatives. This is why we're moving it into a library.
- 13y ago
- rbehrends 13y agoThat's not the original issue. The problem is that the compiler silently predefines an undocumented macro. If the compiler silently predefined an undocumented variable, type, or function called "linux" or "unix", that too can be a problem. E.g., if there were a global function linux() on Linux systems only and you tried to define a global variable linux in your code, you'd get an error, too. The clean solution is to do what clang does and generate informative error messages. In the case of macros, by showing the macro expansion history. For example, the following (using __STDC__ instead of linux for portability): foo.c:2:7: error: expected identifier or '(' int __STDC__ = 1; ^ <built-in>:159:18: note: expanded from here #define __STDC__ 1 Note that there can be other reasons to have macros that are syntactically different from functions and variables, but this isn't one of them.
- kibwen 13y agoBut Patrick isn't responding to the original issue, he was responding to simias' assertion that macros should be syntactically disambiguated. > If the compiler silently predefined an undocumented > variable, type, or function called "linux" or "unix", > that too can be a problem. While we're on the topic of Rust, the compiler actually used to do this! In the early days, the identifiers "error", "warn", "info", and "debug" were all magical, undocumented constants for controlling logging levels. As you might expect, this caused problems. :P So don't think that Patrick isn't keenly aware of the perils of including non-keyword identifiers by default. As for clang, their solution is the best they can do given that they don't have the authority to make the call over whether to syntactically disambiguate macros. It's definitely a debate to be had in the case of a language designed from scratch, and the Rust devs decided to err on the side of explicitness (you can contrast e.g. Elixir, which has the same sort of structural macros but chose not to distinguish their invocation syntax).
- WTFPL 13y agoCompletely agreed.
- WalterBright 13y agoThis is why a major focus of D was to figure out the valuable use cases of the C preprocessor, and instead design replacements for them that are semantically part of the language. On the whole, this has been very successful - about half of D programmers have a C/C++ background and aren't shy about being vocal if there's something they really need.
- lallysingh 13y agoTwo notes that'll date me pretty well: (1) I had to port a game engine to solaris a few years ago. In that engine, if you looked up, you'd see a star emitting light. Guess what 3-letter, all-lowercase identifier was conflicting. (2) Xt (only useful, really, if you're doing Motif work) had a macro or two that looked exactly like a function call, and had a very natural use case for incrementing an index you feed as an argument. If you knew not to do it, no problem. Otherwise, that was a mean bug to track down.
- cpeterso 13y ago> (1) I had to port a game engine to solaris a few years ago. In that engine, if you looked up, you'd see a star emitting light. Guess what 3-letter, all-lowercase identifier was conflicting. sel? The suspense is killing me! :)
- sillysaurus2 13y agoI think it's sol!
- sokoloff 13y agoThe star in question is 93 million miles away... sun
- cpeterso 13y agooh, duh! The phrase "porting to solaris" should have clued me in.. <:)
- X-Istence 13y agoThe other problem is what I call greppability. If I can't grep the header files/source code for a function call I will want to hurt you badly. Especially if a function call is hidden behind 6 - 7 different macro indirections! /me glares over at OpenSSL...