http://qs321.pair.com?node_id=11119462


in reply to On Backwards Compatibility and Bareword Filehandles

The parser raises an error if the new I/O variable would shadow any other bareword known to the parser, so these filehandles cannot conflict with sub names or package names.

There are plenty of ways to get sub names or package names not known to the parser at the point where the parser encounters one of these barewords.

BAREWORD PACKAGE EXPR is a class method call on PACKAGE. Always; even if BAREWORD is "open".

I think the same problem applies here. How is the parser to know that BAREWORD will eventually become PACKAGE, when the package is defined later?

If we allow the precedent of removing features from the language because the pumpking thinks they are ugly...

I think you're being unfair with this characterization. The desire to reduce indirect object notation is not primarily aesthetic; it's primarily because it's too often statically undecidable and it's fragile with regard to the action at a distance of what other code has been parsed and when.

Put another way, if I name a bareword filehandle JSON or YAML because I'm going to read or write some structured data, I don't want my program to behave unpredictably depending on when the modules of the same name get loaded.

Replies are listed 'Best First'.
Re^2: On Backwards Compatibility and Bareword Filehandles
by jcb (Parson) on Jul 17, 2020 at 23:59 UTC
    There are plenty of ways to get sub names or package names not known to the parser at the point where the parser encounters one of these barewords.

    If it is not known to the parser, it cannot cause a parse conflict, and the lexical name prevails for the scope of the block in which it is defined. Existing convention discourages naming subroutines or packages in ALL UPPERCASE anyway, and I do not suggest changing that.

    How is the parser to know that BAREWORD will eventually become PACKAGE, when the package is defined later?

    I had just explained that the parser could recognize PACKAGE tokens because they contain at least one "::". An explicit "::" can be prefixed to top-level packages when needed or assumed when no parse conflict exists.

    The desire to reduce indirect object notation is not primarily aesthetic; it's primarily because it's too often statically undecidable and it's fragile with regard to the action at a distance of what other code has been parsed and when.

    That is a reason to adjust it to make it decidable, not to remove it entirely. Lexical bareword filehandles preserve (most) compatibility while removing (at least some) parse ambiguity: a lexical bareword filehandle parses as VARIABLE rather than BAREWORD. Similarly, recognizing barewords containing "::" as PACKAGE tokens disambiguates class method calls.

    if I name a bareword filehandle JSON or YAML because I'm going to read or write some structured data, I don't want my program to behave unpredictably depending on when the modules of the same name get loaded.

    This proposal does not have that problem — within the scope of a JSON or YAML filehandle, those tokens are variable references rather than package names, but could still be disambiguated as ::JSON or ::YAML to get a PACKAGE token if you wanted to call a class method while inside such a scope. (Note that in the scope of open JSON,..., JSON->new would parse as a method call on the {I/O}JSON variable because part of the proposal is for indirect object and arrow notations to be equivalent.)

      Existing convention discourages naming subroutines or packages in ALL UPPERCASE anyway, and I do not suggest changing that.

      Sure, but I provided two widely-used examples that violate this convention. I could also mention LWP, DBI, CGI....

      An explicit "::" can be prefixed to top-level packages when needed or assumed when no parse conflict exists.

      The problem is I don't know when a parse conflict will exist.

      If I write code with a bareword filehandle in a module, I don't know how that module will be used. I don't know when it will be loaded. I don't know what will be loaded before it and I don't know what will be loaded after it.

      If the parser parses this construct in different ways depending on what's loaded, it's fragile and undecidable.

      An explicit "::" can be prefixed to top-level packages when needed or assumed when no parse conflict exists.

      I don't see how this solves my problem here. I don't want to refer to a top-level package. I want a bareword filehandle (for the sake of argument, at least—I don't want bareword filehandles at all, because of this problem).

      Even if this did solve the problem, I don't think it's worth the tradeoff. Now you've introduced another implicit rule, which is that we must prefix all single-word package names to avoid any potential conflict with a bareword filehandle somewhere.

      I use a lot more package names than I do filehandles.

        The solution is that lexical bareword filehandles exist only in the scopes where they are declared.

        Single-word package only need prefixes within the same lexical scope as an identically-named filehandle. The use of a filehandle YAML in one block does not affect YAML as a package name outside of that block.

        If I write code with a bareword filehandle in a module, I don't know how that module will be used. I don't know when it will be loaded. I don't know what will be loaded before it and I don't know what will be loaded after it.

        This proposal is that bareword filehandles in your module would become lexicals, private to your module.

        If the parser parses this construct in different ways depending on what's loaded, it's fragile and undecidable.

        This is a misunderstanding. Only lexical declarations affect parsing in this proposal, and open BAREWORD,... becomes a lexical declaration. Within the lexical scope of an open YAML,..., YAML would parse as {I/O}YAML and class method calls on YAML would need to be written as ::YAML->method(...) or method ::YAML (...). However, YAML is a bad example here because it has a functional interface, and YAML::DumpFile(...) (for example) is already unambiguous, even if YAML is a lexical filehandle.

        Outside of the lexical scope of open YAML,..., YAML remains a "plain" bareword, and close YAML would, for example, parse equivalently to ::YAML->close — that is, as a class method call on package YAML, whether or not YAML is actually loaded. Obviously, executing a method call to an undefined class is an error, but parsing in this proposal does not depend on the set of loaded packages, only the lexical declarations in effect.

        After thinking about this a bit more, is your concern what happens if a module attempts to use a bareword filehandle defined elsewhere? The I/O operations on undeclared filehandle BAR would parse as class method calls on package BAR. If BAR does not have methods named for I/O operators, you get an error; if BAR does actually have those methods, they will be called (and they will probably end up throwing errors).