Hacker News new | ask | show | jobs
by majewsky 3080 days 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

[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
  }
2 comments

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.