8 ms·
Go’s hidden pragmas
- bla2 9y agoPoor Rob Pike. He always tries to make things simple, and over they time entropy always does its thing. You can hear his frustration in his cited comment.
- pjmlp 9y agoAll languages that tried to fight complexity either grew up to adopt complexity and stay relevant, or faded away. Programming languages don't get complex just for fun, their designers are tackling actual relevant issues. Go community doesn't seem to have learned much from the past.
- Ericson2314 9y agoGo's community doesn't learn from the past, but good languages fight complexity even as they add expressive power. (They at least try to get the most expressive power per complexity cost.) Pragmas, incidentally, aren't really a source of bad complexity. Per the abstract definition of the language, they really indeed do have no effect at all and are just comments. Yay! Implementations have properties too—they aren't just rude practicalities. Compiler's, in particular, connect one language (the input) to another (the output). Programas mediate how those additional properties apply to the language at hand. It's an interesting mental challenge to formalize them in the absence of a compiler having an comcrete stable ABI. So in conclusion, go people once again don't understand good design. Pragmas are not an ugly wart, but actually a great example of layering—a rare example of an abstraction that doesn't leak!
- guntars 9y agoI wouldn't go as far to say that having undocumented magic comments doesn't add complexity. From a very surface level, sure, the parser is the same, but now every tool that works with the Go language needs to be aware of these. Linters, for example, need to not complain about the missing space in front of the comment, but only if the comment starts with "go:". Ultimately anything that changes how the program is executed is going to add complexity, so they might as well "make it official" and add a keyword for it.
- zzzcpan 9y agoIn Perl and Javascript pragmas are language level and are used to help people avoid some mistakes at certain stages of software development. This is fine, no leaky abstractions. In Go they are lower level and therefore are side effects of leaky abstractions in compiler and language design. So they should be fixed, not kept or turned into pragmas in the spec. The choices I can think of: either make Go lower level itself or move low level stuff into another intermediate lower level language.
- jerf 9y agoThere's another choice, which is to keep doing things the way they are being done, simply not add another 20 pragmas, and get on with life because there isn't actually a problem here. None of the problems with pragmas I've seen in other languages are present in Go, since the pragmas are simple and mostly used only by the implementation and/or compiler itself, and there's no interactions, or massive code complexity from ifdefs, or string-concatenation-based macro disasters, or any of the other real problems caused by pragmas, with the possible faint exception of pragmas not being cleanly delineated from the comment syntax, which is still not causing any huge problems I can see, nor is that likely to change in the future. The problems that C has with pragmas, and that C++ imported from pragmas, can not be naively imputed to other languages without demonstrating there's actually a problem here. This wouldn't even make my top 10 issues with Go; I'm not sure it's even an issue at all.
- majewsky 9y ago> In Perl [...] pragmas are language level No, actually. The syntax that people use to invoke the pragma ("use strict [arg]...") is not a pragma at the language level, it's just the syntax for importing symbols from modules. For example, use strict ('vars', 'refs'); expands to BEGIN { require 'strict'; strict->import('vars', 'refs'); } because that's how the "use" statement is defined. `BEGIN{...}` cause the statements in the block to be executed as soon as the BEGIN block has been fully parsed [1]. `require 'strict'` loads the module `strict.pm` from the library path (the source code is on CPAN at [2], if you're interested), then its `import()` method is called with two string arguments. The implementation of that method is: sub import { shift; $^H |= @_ ? &bits : all_bits | all_explicit_bits; } There's a lot of weird Perl syntax in there, but the gist is that it modifies the $^H variable. And THAT is the actual pragma which is defined by the language. [3] The module strict.pm is just a wrapper around $^H to make things a bit more user-friendly. I know that's sorta kinda off-topic, but since we're talking about language design, I figured I'd contribute this small anecdote that illustrates really well how the more recent parts of Perl are designed: a ton of metaprogramming on top of relatively small changes to the core language. If you want another example, have a look at how object-oriented programming was tacked on to Perl as a tiny afterthought, yet the way it interacts with all the other parts of the language makes hugely powerful OOP frameworks like Moose possible. (OTOH, that approach also makes the language pretty messy, but it always gets the job done for me, at many scales.) [1] Usually, execution only begins when the entire file has been parsed, but this code needs to run earlier because it changes the parser's behavior. [2] https://metacpan.org/source/SHAY/perl-5.26.1/lib/strict.pm https://metacpan.org/source/SHAY/perl-5.26.1/lib/strict.pm [3] Notably, $^H behaves differently from other variables: Every assignment to it is scoped only to the current block, whereas regular variables need to be shadowed explicitly. This is particularly useful to temporarily lift a strictness requirement for a single statement, similar to how `unsafe` is used in Rust: use strict; ... my $function_name = 'implementation_' . ($x + 2 * $y); $function_name(); //error: cannot call string value { no strict 'refs'; //"no" is like "use", but in reverse (calls the module's unimport() instead of import()) $function_name(); //works: calls the function with the name stored in the variable }
- toprerules 9y agoThis is true for languages that try to be all things to all people (a la Java). All languages are DSLs, and if you target just a few specific domains and beat back the masses who want the language to expand beyond its intended purpose, than simplicity remains possible.
- crdoconnor 9y agoDSLs shouldn't be turing complete and turing complete languages shouldn't try to be DSLs. Ant was a DSL that managed to become turing complete and the results were pretty horrible.
- nerdponx 9y agoTuring completeness is a symptom, not a cause. No one would argue that SQL is bad because some implementations are Turing-complete [0]. [0]: https://stackoverflow.com/a/7580013 https://stackoverflow.com/a/7580013
- crdoconnor 9y ago>No one would argue that SQL is bad because some implementations are Turing-complete They do actually. Though when people do say that it tends to be phrased "keeping business logic in stored procedures is a bad idea". People argue that all the damn time. Accidental turing completeness usually signals a design flaw somewhere (would you also consider it too controversial to argue that C++ templates mentioned in your link are nasty and people complain about them a lot?).
- PopsiclePete 9y ago>All languages that tried to fight complexity either grew up to adopt complexity and stay relevant, or faded away. And yet, to this day, C is just as, if not more popular, than C++. Why is that? I can do so much more in C++, but I, and my colleagues, pick plain-old C every time.
- pcwalton 9y agoYou're in the minority. For new projects, C is much less popular than C++.
- marrs 9y agoIt might not be that easy to tell. The C++ I write (for myself) is essentially plain C. No templates, dynamic dispatch, constructors, or exceptions. Most of the standard libs I use begin with the letter 'c'. It uses some C++ features, but it's philosophically much closer to C code.
- pjmlp 9y agoIf it only compiles with a C++ compiler, it is C++, regardless of the amount of language features being used.
- marrs 9y agoThat's fine, but I don't think that's what people mean when they say C++. I certainly wouldn't call myself a C++ coder and, if I applied for a C++ job, I'm pretty sure that, after I had explained that I don't do exceptions, virtuals, or the STL, I'd be politely shown the door.
- pjmlp 9y agoThanks Linux. C was already on the way out when Linus created Linux. Apple was migrating from Object Pascal to C++. IBM had CSet++ for OS/2. Borland, Microsoft, Zortech, Symatec were selling C++ frameworks. UNIX vendors were playing with Taligent and CORBA. BeOS and Symbian were developed in C++. Then came Linus, made Linux with GNU on top. GNU project for a long time always mentioned that the go to language for GNU projects should be C. All major C compilers are written in C++ nowadays, there is hardly any reason to stay with C outside UNIX world.
- Thaxll 9y ago> Programming languages don't get complex just for fun, their designers are tackling actual relevant issues. Have you ever used C++ templates? I mean every popular languages have issues related to design complexity.
- marcosdumay 9y agoSome complexity is avoidable, other isn't. Besides C++ templates are a result of creating a conceptually simple, one size fits all solution for generics, metaprograming, library tuning, and some dozens of other problems that other languages have specialized tools to solve. Turns out that the complex set of features works better.
- stcredzero 9y agoIs it a complex set of features, or rather a set of focused tools?
- marcosdumay 9y agoIt is a large set of simple tools. It is conceptually complex because each tool is different and you must learn them all.
- pjmlp 9y agoI did my first C++ steps with Turbo C++ 1.0 for MS-DOS. My first use of C++ templates was in Turbo C++ 3.5 for Windows 3.1.
- deleted 9y ago[deleted]
- dmitriid 9y agoGuy Steele, "Growing a Language", https://www.youtube.com/watch?v=_ahvzDzKdB0 https://www.youtube.com/watch?v=_ahvzDzKdB0
- AnimalMuppet 9y ago> All languages that tried to fight complexity either grew up to adopt complexity and stay relevant, or faded away. > Programming languages don't get complex just for fun, their designers are tackling actual relevant issues. Yes and yes. > Go community doesn't seem to have learned much from the past. Well... if you start with something simple, and complexity comes, you can still try to keep it as simple as possible. But if you started with something that was already complex (but complex in ways that your theory said it needed to be, not in the ways that the real world said it needed to be), and you try to fix that, you wind up with something really complicated. Ditto if you start off with complexity to handle all the use cases of the past. Go started off simple, and is letting real-world use push them into becoming more complicated. That's a defensible approach, even today.
- lurr 9y ago> That's a defensible approach, even today Not if you are dishonest about it. Not if you refuse to learn from the past couple decades.
- AnimalMuppet 9y agoIn what way is Go dishonest about it? In what way have they refused to learn from the past couple decades?
- lurr 9y agoI think insisting that they need more use cases for generics is dishonest when you consider they used generics to implement library data types themselves. I don't mean they are outright lying, or are bad people or anything like that.
- Veedrac 9y agoI suspect the Go developers just have a very different idea of what the past is. Given the state of software engineering, I'm not quite as likely to put a positive spin on the things we've built since then.
- earenndil 9y agoLisp?
- warent 9y agoHis concern was directed at the magic comments though. I don't understand why they didn't just create new syntax for pragmas since they're already parsing something. e.g. #noescape # could be treated as syntactic sugar for //go: until v2 if they want
- deleted 9y ago[deleted]
- Avshalom 9y agoYeah but Rob Pike's idea of simplicity is him personally not having to implement things. If every one else in the world has to implement the same thing a thousand times a day he still thinks his thing is simple.
- stcredzero 9y agoIf every one else in the world has to implement the same thing a thousand times a day he still thinks his thing is simple. Smalltalk all over again!
- pjmlp 9y agoActually even Smalltalk is more feature rich than Go.
- stcredzero 9y agoThe community had a real "Not Invented Here" problem in our earlier years, which we never really overcame.
- pjmlp 9y agoI see, as I only used Smalltalk during university for project assignments (Digitalk Smalltalk/V), I never got to experience that.
- stouset 9y agoI wish I could upvote this comment a dozen times. Much of go’s “simplicity” is a Faustian bargain that comes at the cost of unnecessary complexity in each and every project that winds up being written with it.
- tapirl 9y agoThis is called trade-off. The reality world is never perfect.
- saagarjha 9y ago> Given the race detector has no known false positives, there should be very little reason to exclude a function from its scope. Performance, maybe?
- vardump 9y ago> Performance, maybe? Performance for running race detector (debug) binaries? Are you worried slower code hides a race? I can't think of a reason to ship or use race detector compiled binaries in production. Or do you have something in mind?
- saagarjha 9y agoYeah, that’s what I was getting at. Race condition detection would probably slow down your code, so you probably wouldn’t use it in production.
- LukeShu 9y agoRace detection is totally disabled for production binaries, because it does slow down the code. The question is: For debug builds that explicitly have the "-race" flag passed to the compiler, why would you want to disable race detection for a specific function?
- nemo1618 9y agoI actually do have a real example of this. We use -race during our automated testing. The setup for some of our tests involves CPU mining (rapid blake2b hashing). This code definitely doesn't have any races, and it runs waaaaay slower when race detection is enabled. So we could speed up our tests significantly by disabling race detection just for the setup phase.
- hmmdar 9y agoHave you considered enabling parallel tests for that package? It let's test functions run in parallel with each other. Might address some of the issue with the performance.
- cjslep 9y agoGo's "pragmas" are already exposed to the developer via go generate [0]. Edit: Granted, this is not a directive for the compiler though. [0] https://blog.golang.org/generate https://blog.golang.org/generate
- nerdponx 9y agoThis seems like one arugment to be made in favor of giving the language first-class access to the compiler, à la Lisp.
- nemo1618 9y agoNot at all! This a common impulse among programmers: upon seeing a specific case, you want to make it as abstract and generic as possible. This maximizes the power and flexibility available to the programmer. However, the design philosophy of Go pulls in the exact opposite direction! Go emphasizes simplicity and large-scale engineering -- i.e. standardization. (gofmt is the canonical example of this.) Giving programmers the power to manipulate the compiler in arbitrary ways would be a nightmare for Go's designers. It opens the door to "clever" code that you tear your hair out debugging 6 months later. The strength of Go is precisely that it makes it difficult to write "clever" code. Go looks pretty much the same everywhere. Which is a little boring, sure; but for me (and many others), that's an acceptable trade-off.
- pjmlp 9y agoI rather prefer the Ada, Eiffel, Delphi, C++, Java and C# decisions regarding large scale engineering.
- sifoo 9y agoYeah, keep telling yourself that. Do you actually write code to solve problems yourself or are you more into paying others to do the same? I honestly don't know which alternative would be worst here. What I do know is that this attitude isn't serving us. We're in the business of solving really tricky problems, forcing ourselves to do that using dumbed down tools to protect us from our own intelligence and creativity is insane.
- dom96 9y agoThis is what Nim[1] does. Its pragmas are extensible via macros[2]. 1 - https://nim-lang.org/ https://nim-lang.org/ 2 - https://nim-lang.org/docs/manual.html#macros-macros-as-pragmas https://nim-lang.org/docs/manual.html#macros-macros-as-pragm...
- tapirl 9y agoThe pragmas in Go are not intended to be used in general user code. They should be only used in Go runtime implementation and standard packages. The pragmas in Go are just some hints for compilers. Compilers will ignore many of the pragmas used in custom user code.
- buro9 9y agoExcept for the build conditional flags: https://golang.org/pkg/go/build/#hdr-Build_Constraints https://golang.org/pkg/go/build/#hdr-Build_Constraints Which are pretty useful when you want to target tests to different versions of Go when std lib exhibits different behaviour (behaviour changed as std lib matured).
- vorg 9y agoAlso when you want to put package-main files in a library package directory, you need to put // +build ignore near the top of it.
- lurr 9y agoSo these features were totally not useful enough to be part fo the language, but you can't even build the compiler without them. Just like generics is totally useful enough to be used by parts of the runtime, but when it comes to including them in the lanuage they can't even think of a use case.
- jonathanstrange 9y agoPragmas are always a bad idea. The Ada community has learned that the hard way. Whatever the pragma does, it should be part of the language standard and never be implementation-dependent. It's time that language designers include language pragmatics in the core language. That includes for example big O information about data structures, packing of structures, alignment properties, memory access information, etc. Currently, in most if not all languages this information is spread all over levels, from nonstandardized compiler flags over pragmas up to the core language. It's a huge mess.
- kemitche 9y agoI don't know if I agree. The pragmas listed in the article, by and large, are directives at specific parts of the reference go implementation, allowing for specific optimizations/annotations that implementation needs. Any other implementation of go seems like it could safely ignore those directives.
- masklinn 9y ago> Pragmas are always a bad idea. The Ada community has learned that the hard way. Whatever the pragma does, it should be part of the language standard and never be implementation-dependent. > It's time that language designers include language pragmatics in the core language. That includes for example big O information about data structures, packing of structures, alignment properties, memory access information, etc. So pragmas are "always a bad idea" but you should have them "in the core language"… Don't you feel your comment is pretty contradictory? A pragma is a directive for the system (mostly compiler), that's orthogonal to it being implementation-specific.
- jonathanstrange 9y agoYou misread my comment. The functionality offered by pragmas must be mandatory and in the core language, whether you call them pragmas or not. Everything else leads to problems. It's true that if pragmas were all fully specified in the core language and not optional, then they wouldn't pose any problems. In reality, however, some pragmas are regulated by the core language and others are implementation specific additions. The result is a huge mess, it's the #1 source of incompatibility of standardized languages like Ada. Even just having optional pragmas in the core language is problematic, because at one point or another developers will start relying on the optional functionality to do something that one implementation does and another doesn't. Optional optimization and packing directives are typical examples. In theory they shouldn't be able to break programs, in reality they do.
- yokohummer7 9y agoI hated the idea of using comments as directives when Go 1.4 introduced //go:generate. But, holy, they were there from the beginning? They bring back my painful memories of the old days when I had to use conditional comments to support IE6...
- klodolph 9y agoComments are also interpreted by go build, to choose which files to build on each platform. Memories of IE6 are painful because of the deviation from the standard, but with Go you don’t have that problem.
- Krabby127 9y agoPossibly off topic, but Verilog has something similar. //read_comments_as_hdl_on is a thing and it makes it a pain.
- majewsky 9y agoIE's conditional comments were actually a pretty elegant solution given that you had to be compatible with every other HTML parser out there. The painful memories that I recall are about IE 6/7 itself, not about conditional comments.
- marcosdumay 9y agoEvery higher level language has directives nowadays, and those almost every time encoded in comments. Honestly, between documentation, compiler pragmas, linter directives, packaging and linking instructions, and etc, we are getting into a point where languages will need to specify something like "comments starting with this string must be ignored".
- lurr 9y ago> and those almost every time encoded in comments which lanugages do that?
- pcwalton 9y agoFor once, I'm gonna be the one sticking up for Go here. :) Pragmas or annotations are kind of unavoidable, and I don't think that it was a mistake to include them. I wouldn't have used comment syntax, but whatever; that's a minor quibble. Actually, I wish Rust had done one thing that Go did: namespacing the pragmas. That would have been a more future-proof thing to do, because macros effectively give us user-definable pragmas, and once you have user-definable pragmas you have name collision problems. Kudos to the Go team for being forward-thinking there. I suspect we'll have to figure out some kind of solution here, but it won't be as pleasant as if we had just used namespaces in the first place.
- Royalaid 9y agoThis does sound like a really good idea. Have you made an RFC or proposal to the team to see if it is possible?
- kibwen 9y agoAs I mention in a sibling comment, the RFC to make macros work with the module system like any other item has not only been accepted, but mostly implemented. :)
- brandonbloom 9y agoYou've missed the chance to include namespaces on the standard set, but is it too late to reserve un-namespaced annotations for official use?
- kibwen 9y agoIt's not too late. The Rust standard library has what's called the "prelude", which is a set of items (functions, types, traits) that are imported by default into every Rust program. So for example the complete Rust program `fn main() { let x = String::new(); }` works despite the fact that at no point did we import any type named `String`; this is because Rust programs implicitly link the stdlib by default, and the stdlib publicly exports the items in the prelude (https://github.com/rust-lang/rust/blob/master/src/libstd/prelude/v1.rs https://github.com/rust-lang/rust/blob/master/src/libstd/pre...). So in the future when macros are namespaced just like every other item (which is actually already designed and largely implemented, see my other comments here for links), all that needs happen is to export the newly-transitioned macros from the prelude and all will continue to work as usual. As for potential collisions with third-party macros, the old system already requires anyone who wants to import macros to stick a hacky "macro_use" pragma on their import statement, and old-style macros are specified to shadow rather than collide so there will be no need to be cautious with updating the stdlib. Third-party libs will be free to update to "macros 2.0" at their leisure (though the need to have users explicitly import macros will require those libraries to issue a breaking change when they do so), and old-style macros will be supported for quite a while though eventually they will be deprecated and discouraged (and presumably removed in some future epoch).
- tidwall 9y agoThe page is missing the best one of them all //go:linkname, which allows for linking in private functions from other packages. Including the Go runtime. For example: https://github.com/tidwall/algo/blob/master/algo.go https://github.com/tidwall/algo/blob/master/algo.go
- nickcw 9y agolinkname is brilliant thanks! I've done that sort of thing with an assembler shim in the past.