Hacker News new | ask | show | jobs
by zzzcpan 3080 days ago
In 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.
2 comments

There'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.

> 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

[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
  }
Sure, this is all correct from implementation perspective as levels are not strictly defined and are open to interpretation. However, we were talking about users' perspective, which is kind of the whole point of leaky abstractions. In this context language level features are those that users can understand about the language without any assumptions about other levels, like how compilers represent things internally.
The word pragma in perl has always been used to refer to that specific syntax sugar, just read the start of "perldoc perlpragma".

Furthermore the $^H (there's also %^H) facility you mention is just one way pragmas are implemented, e.g. "use overload" is a core pragma that doesn't use that method at all, instead it defines special functions in the importing package which the compiler is aware of.

Then there are other "pragmas" that are really just utility wrapper functions, e.g. "use autouse". The "Pragmatic modules" section of "perldoc perlmodlib" has the full list.