Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compilation error only when using the repl

I am getting an error only when the code is entered line by line in the repl. It works when the whole program is pasted at once, or from the command line.

class A {
    method a () {
    return 1;
    }
}

class B {
    method b () {
    return 2;
    }
}

This is the error statement:

===SORRY!=== Error while compiling:
Package 'B' already has a method 'b' (did you mean to declare a multi method?)

This screen shot might make it clearer. On the left I just pasted the code, and on the right I entered it line by line. The code is still working but what is causing the error?

pasting vs line-by-line

For some reason, I could not reproduce this using just one class.

like image 715
rajashekar Avatar asked Jul 18 '21 07:07

rajashekar


Video Answer


2 Answers

I can reproduce that error, and looks like a REPL bug, or simply something the REPL is not prepared to do. This, for instance, will also raise an exception:

class A {
    method a() {
    return 1;
    }
};

class foo {
    has $.bar = 3;
};

In either form, either pasting it directly or in pieces. It's always the second class. It's probably related to the way EVAL works, but I really don't know. At the end of the day, the REPL can only take you so far and I'm not totally sure this is within the use case. You might want to use Comma or any other IDE, like emacs, for anything that's more complicated than a line; Comma also provides help for evaluating expressions, and even grammars.

like image 54
jjmerelo Avatar answered Oct 23 '22 03:10

jjmerelo


I think Comma is the bees knees. And I almost never use the repl. But I enjoy:

  • Golfing Your example is a more than adequate MRE. But I love minimizing bug examples.

  • Speculating I think I can see what's going on.

  • Searching issue queues Rakudo has two issue queues on GH: old and new.

  • Spelunking compiler code Rakudo is mostly written in Raku; maybe we can work out what this problem is in the REPL code (which is part of the compiler)?

Golfing

First, the bug:

Welcome to 𝐑𝐚𝐤𝐮𝐝𝐨™ v2021.03.
Implementing the 𝐑𝐚𝐤𝐮™ programming language v6.d.
Built on MoarVM version 2021.03.

To exit type 'exit' or '^D'
> # 42
Nil
> { subset a
* 
===SORRY!=== Error while compiling:
Redeclaration of symbol 'a'.
at line 3
------> <BOL>⏏<EOL>

Commentary:

  1. To get on the fairway, enter any line that's not just whitespace, and press Enter.

  2. Pick the right iron; open a block with {, declare some named type, and press Enter. The REPL indicates you're on the green by displaying the * multi-line prompt.

  3. To sink the ball, just hit Enter.


Second, golfing in aid of speculation:

> # 42
Nil
> { BEGIN say 99
99
* }
99
> 

(BEGIN marks code that is to be run during compilation as soon as the compiler encounters it.)

Speculating

Why does the initial # 42 evaluation matter? Presumably the REPL tries to maintain declarations / state (of variables and types etc) during a REPL session.

And as part of that it's presumably remembering all previous code in a session.

And presumably it's seeing anything but blank lines as counting as previous code.

And the mere existence of some/any previous code somehow influences what state the REPL is maintaining and/or what it's asking the compiler to do.

Maybe.


Why does a type declaration matter when, say, a variable declaration doesn't?

Presumably the REPL and/or compiler is distinguishing between these two kinds of declaration.

Ignoring the REPL, when compiling code, a repeated my declaration only raises a warning, whereas a repeated type declaration is an error. Quite plausibly that's why?


Why does a type declaration have this effect?

Presumably the type successfully compiles and only after that an exception is thrown (because the code is incomplete due to the missing closing brace).

Then the REPL asks the compiler to again try to compile the multi-line code thus far entered, with whatever additional code the user has typed (in my golf version I type nothing and just hit Enter, adding no more code).

This repeated compile attempt includes the type declaration again, which type declaration, because the compilation state from the prior attempt to compile the multi-line code is somehow being retained, is seen by the compiler as a repeat declaration, causing it to throw an exception that causes the REPL to exit multi-line mode and report the error.


In other words, the REPL loop is presumably something like:

  1. As each line is entered, pass it to the compiler, which compiles the code and throws an exception if anything goes wrong.

  2. If an exception is thrown:

    2.1 If in multi-line mode (with * prompt), then exit multi-line mode (go back to > prompt) and display exception message.

    2.2 Else (so not in multi-line mode), if analysis (plausibly very basic) of the exception and/or entered code suggests multi-line mode would be useful, then enter that mode (with * prompt). In multi-line mode, the entire multi-line of code so far is recompiled each time the user presses Enter.

    2.3 Else, display exception message.

  3. (Obviously there's something else going on related to initialization given the need to start with some evaluation to manifest this bug, but that may well be a completely distinct issue.)

Searching

I've browsed through all open Rakudo issues in its old and new queues on GH that match 'repl'. I've selected four that illustrate the range of difficulties the REPL has with maintaining the state of a session:

  • REPL loses custom operators. "Interestingly, if a postfix operator like this is exported by a module which is loaded by the REPL, the REPL can successfully parse that operator just once, after which it will fail with an error similar to the above." Is this related to the way the bug this SO is focused on doesn't manifest until it's a second or later evaluation?

  • Perl6 REPL forgets the definition of infix sub. Looks like a dupe of the above issue, but includes extra debugging goodies from Brian Duggan. ❤️

  • REPL messes up namespaces when Foo is used after Foo::Bar.

  • In REPL cannot bind to scalars declared on earlier lines.

One thing I haven't done is checked whether these bugs all still exist. My guess is they do. And there are many others like them. Perhaps they have a somewhat common cause? I've no idea. Perhaps we need to look at the code...

Spelunking

A search of the Rakudo sources for 'repl' quickly led to a REPL module. Less than 500 lines of high level Raku code! \o/ (For now, let's just pretend we can pretty much ignore digging into the code it calls...)

From my initial browser, I'll draw attention to:

  • A sub repl:

    sub repl(*%_) {
      my $repl := REPL.new(nqp::getcomp("Raku"), %_, True);
      nqp::bindattr($repl,REPL,'$!save_ctx',nqp::ctxcaller(nqp::ctx));
      $repl.repl-loop(:no-exit);
    }
    

    Blame shows that Liz added this a couple months ago. It's very tangential to this bug, but I'm guessing methods and variables with ctx in their name are pretty central to things so this is hopefully a nice way to start pondering that.

  • method repl-eval. 30 lines or so.

  • REPL: loop { ... }. 60 lines or so.

That'll do for tonight. I'll post this then return to it all another day.

like image 20
raiph Avatar answered Oct 23 '22 05:10

raiph