Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Re^3: What is an ordinary statement?

by haukex (Archbishop)
on Jun 05, 2019 at 14:54 UTC ( [id://11101017]=note: print w/replies, xml ) Need Help??


in reply to Re^2: What is an ordinary statement?
in thread What is an ordinary statement?

Does the compiler do inlining?

Very rarely, because Perl is such a dynamic language, variables and even things such as subroutine declarations can change at any time and at a distance (kind of like volatile variables in C). The only place I can think of inlining off the top of my head is Constant Functions, the rest is optimization that Perl may or may not do internally (such as constant folding), but that should not be visible to the user.

But in general, it sounds to me like you might be drawing too many parallels between Perl and a truly compiled language such as C/C++, where there are things such as generating a binary and a very clear distinction between compile time and run time. In the Perl interpreter, the lines between the traditional "compile time" and "run time" can be blurred - you can run code at compile time with BEGIN and use, and compile and run code at runtime with eval, do, and require. "Compilation" is really just translating source code into the in-memory opcode tree, that the interpreter then executes (Update: remember that the perl binary is both the compiler and interpreter, which is why it can do the aforementioned flexible compile-/run-time things).

For example, here's a common mistake:

our $FOO = 1; use if $FOO, 'SomeModule'; print $FOO, "\n";

This will compile fine and print 1, but SomeModule won't be loaded! What is going on here is that use is equivalent to a BEGIN block, which is code that will be executed during the compile time of the code. So our $FOO = 1; declares a variable $FOO, so that it can be used in the following use statement, but the assignment $FOO = 1 doesn't actually happen until runtime, at which time the use statement will have already finished executing. To fix this, one needs to write e.g. our $FOO; BEGIN { $FOO = 1 } to move the assignment into the compile time as well, before the use executes.

May I ask what the background of these questions is - is it just curiosity, or are you trying to implement something specific, or are having problems with something?

Replies are listed 'Best First'.
Re^4: What is an ordinary statement?
by RonW (Parson) on Jun 06, 2019 at 23:04 UTC
    it sounds to me like you might be drawing too many parallels between Perl and a truly compiled language such as C/C++, where there are things such as generating a binary and a very clear distinction between compile time and run time.

    Perl is not as different as you suggest.

    perl compiles Perl code into Perl op codes and operands which are then executed by the Perl virtual machine (PVM).

    In theory, the PVM could be implemented in hardware. Many of the Perl op codes correspond to common CPU opcodes. However, the operands are structures. And many of the op codes are more like "system traps" that call a system API function. The PVM is high level machine.

    But, it is possible to generate a "binary" file. The Bytecode backend writes Perl bytecode to a file, which the ByteLoader can feed into the PVM.

    One of the major problems with this is that if code in BEGIN blocks or in use'd files has "run time side effects", those won't happen when you run the previously compile program.

    Bytecode isn't the only option for saving compiled Perl. The Simple C backend encodes Perl's op codes and operands into C source that, when compiled by a C compiler will produce an executable that feeds the compiled Perl code into the PVM. The optimized C backend attempts to de-compile the Perl ops and operands into C code.

    But, again, any code in BEGIN blocks or in use'd files won't be run.

    If a Perl program is written to have no "run time side effects" at compile time - and only uses modules that are similarly coded, then storing a compiled Perl program in a file for later execution is an option.

      Perl is not as different as you suggest.

      You are of course correct that Perl "bytecode" can be saved and restored (with some limitations), and taken individually, I agree with many of the statements you've made and they are useful additional information. However, in the context of this thread, and because it appears you're presenting them as a counterpoint to what I was saying, I have to strongly disagree.

      the Perl virtual machine (PVM)

      I've never heard the term PVM before and it is used nowhere in the Perl core or documentation. You'll find only a single reference in the Camel: "the Perl virtual machine (which we refuse to call a PVM)".

      In theory, the PVM could be implemented in hardware.

      In theory, any piece of software could be implemented in hardware.

      The optimized C backend attempts to de-compile the Perl ops and operands into C code.

      I think "attempts" is a key word here. Although I admit I haven't worked with this C backend, I am also not aware of any implementation of compiled Perl that is able to do everything that perl can.

      If a Perl program is written to have no "run time side effects" at compile time

      In other words, if I omit exactly those dynamic features that I was making a point of, then I no longer have a point :-P You also make no mention of eval, which is quite significant here as well.

      If I compile a C program, the binary it generates is entirely independent from the compiler, it can be copied onto a machine with no compiler at all, and it is executed directly by the hardware. Perl code is executed by its interpreter, which runs alongside the compiler, so the Perl compiler has the ability to reach directly into the interpreter, and the Perl interpreter has the ability to directly reach into the compiler, simply because it is always there. Of course, in theory it's possible to link a C compiler into a C program, and maybe it's possible to have dynamically compiled C interact seamlessly with previously compiled and currently running C code, and maybe it's possible to have C code affect the complication phase of the surrounding C code, but at some point, I could just as well link perl, lua, python, etc. into my C program.

      To bring this back into the context of the thread: how the compiler and interpreter interact in regards to declarations. Consider this example:

      Foo.pm:

      package Foo; use warnings; use strict; use Exporter 'import'; our @EXPORT = qw/foo $FOO/; sub foo { print @_, "\n" } our $FOO = 'bar';

      test.pl:

      use warnings; use strict; use Foo; foo $FOO;

      In test.pl, strict is enabled and I make no prior mention of the function foo or the variable $FOO, yet I am able to use them. This is because use Foo; is essentially equivalent to BEGIN { eval ... } - during the compilation phase of test.pl, perl loaded another file, compiled it, executed it all the way through, and this execution of Foo.pm affected the further compile time phase of test.pl, allowing the compiler to distinguish that both foo and $FOO are valid here.

      Taking it a step further:

      use warnings; use strict; eval( rand() < 0.5 ? q{ sub foo { print "Hello!\n" } sub quz { print "Quz!\n" } } : q{ sub foo { print "Perl!\n" } sub baz { print "Baz!\n" } } ); foo(); # will it print "Hello!" or "Perl!" ? quz(); # will it die or won't it?

      I maintain that this is fundamentally different from how C and similarly compiled languages operate.

      Update: Minor edits to wording.

        True, I didn't mention eval. At least in the case of the "optimized C" back end, I should have. Programs that use eval and want to use this back end would still need to link to perl.dll or perl.so to run. Also, there may be other features of Perl impractical to translate without relying on perl.dll/perl.so

        As for Perl bytecode requiring a run time interpreter to execute the bytecode, so does Java bytecode.

        As for "run time side effects", I mean does a BEGIN block perform an action that only makes sense at run time.

        For example, suppose there's a Expect::Quick module and you use it like:

        use Expect::Quick Prompt => [ -re => '^\$\s+' ], REMOTE => 'user@examp +le.com'; send 'command param1 param2'; my $resp = before; ...;

        The implicit BEGIN block:

        BEGIN { require Expect::Quick Prompt; import Prompt => [ -re => '^\$\s+' ], REMOTE => 'user@example.com' +; }

        will create Expect obj, open a connection to example.com and login as "user".

        If you saved the bytecode then load and run it later, the connection to example.com will not be done.

        On the other hand:

        use Expect::Quick; connect Prompt => [ -re => '^\$\s+' ], REMOTE => 'user@example.com'; send 'command param1 param2'; my $resp = before; ...;

        could be compiled and the bytecode saved. Then later, when loaded and run, it would work correctly.

        The dynamic features of Perl would still be available to use.

        Of course, it is possible for the actual connect and login to be deferred until the first time send is called. My point is that import would have to be written to do that. And any other BEGIN blocks, implicit or not, could not perform actions that need to be performed at run time. But any and all of Perl's compile time magic is still "in play".

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11101017]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (6)
As of 2024-04-19 03:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found