bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: It is possible to remove the readonly attribute from {BASH, SHELL}OP


From: Martin Kealey
Subject: Re: It is possible to remove the readonly attribute from {BASH, SHELL}OPTS
Date: Sun, 25 Feb 2024 21:03:59 +1300 (NZDT)
User-agent: Alpine 2.21 (DEB 202 2017-01-01)

On Fri, 23 Feb 2024, Robert Elz wrote:
>   | Yes, that's exactly the point, to *avoid* dynamic scoping. I want the
>   | equivalent of Perl's "my", rather than Perl's "local".
>
> Lexical scoping does not, that is, cannot, work with the shell
> language as it is defined, if you want that you need to go and
> invent a whole new language.

I'm beginning to wonder if people are reading some different proposal from
what I wrote.

I'll say it again: unless a variable is explicitly marked as 'lexically
local' (however that's achieved) then it would continue to behave as it does
now. Any lexically local symbol table would only be consulted when an
appropriate declaration has been encountered.

You already know I'm keen on maintaining backwards compatibility, even for
stuff that appears at first blush to be "just a stupid bug", so if it sounds
like I'm suggesting breaking existing code, either I haven't explained it
properly, or the reader hasn't understood what I've written, or both.

> As it is thare are too many places in sh where correct operation
> requires access to specific vars, which makes no sense (ie: would
> not work as intended with lexically scoped local vars) - consider
> PATH CDPATH PWD OPTIND OPTARG REPLY (for shells that have it)
> and try to explain how they can be made to work as intended if
> made local, with lexical scoping.

Sure, and since you're not declaring them as lexicals, there's no problem
continuing with that.

(If someone declares a lexical variable that masks a reserved name, well
they get what they ask for. That doesn't make my entire proposal broken.)

> Regardless of how much people have tried to attempt to impose a more
> programming language model on sh vars, it simply does not work.

If you *take away* the current 'local', then of course it's not going to
work. That is not what I'm proposing.

> sh was designed [...]

Let's face it, sh was barely 'designed' at all; rather, it was defined by
"whatever the implementation does", and *that* was "mash together a macro
processing language, a batch processing language, and a command line
interface, and try and make something coherent".

I've mentioned Perl repeatedly because it provides excellent prior art for a
language moving from dynamic lifetime to lexical scope, while still keeping
the former as an option. The existing guts of Bash make it a bigger job than
if we were simply starting with a sane "programming" model, but it's
certainly not impossible.

>   | new code can avoid the craziness that comes from "everything is global,
>
> crazy or not, that is the sh model.  If you don't like it, use
> something different -- tclsh (or with graphics added, wish) is much
> more rigorous, and also much less friendly interactively.

Another way to think about this is that Bash already has "enable -f" that
can load a new function that was written in C and compiled to a binary
shared library.

What I'm proposing amounts to a new way to define functions, similar to ones
loaded by "enable -f", except that (a) they're interpreted rather than
complied, and (b) they haves almost the same syntax as the normal shell, but
(c) their internal variables can be completely hidden from the rest of the
program/script.

When such a function needs to reach out and touch a variable in the global
namespace, arguably it should have to try just a little harder to do so.

By "trying harder", I mean for one of these new-style functions to access an
outer variable Foo, you might need to 'import' it with a declaration, or you
might need to qualified the name where it's used, perhaps by adding a
prefix.

Here's an example I've just dreamed up (loosely borrowing some syntax from
ksh) simply to prove that this is possible:

* to reach up to the root of the function call stack ("main" or "global"):
 * .top.Foo=newvalue
 * ${.top.Foo}

* to reach into the calling function's local variables:
 * .caller.Foo=newvalue
 * ${.caller.Foo}

* to reach outside the innermost dynamic local (however far up the function
call chain that might be):

 * .local.out.Foo=newvalue
 * ${.local.out.Foo}

(Note that Perl simply defines that a list of two dozen or so built-in
variables are forced into the 'main::' package; the equivalent here would be
to make an exception (to the "trying harder" rule) for the existing special
variables, including any future additions whose name starts with 'BASH'.)

* to reach the variables of a lexically enclosing function:
 * .outer.Foo=newvalue
 * ${.outer.Foo}

I'm in two minds whether to 'fall back' to the global scope if there are no
lexical matches; obviously if it's not marked as a new-style function, then
the answer must be yes; but if it *is* recognisable as a new-style function,
sound software engineering principles would come down firmly on 'no'.

>   | even when we claim it's local" and "unset can even mess with the poor
>   | protection afforded by 'local'".
>
> I'm guessing you mean bash's bizarre unset definition wrt locals.
> I consider that to just be a bug.

Yes that's the one, and I agree it's a bug.

But now it's also an implementation "feature" that people may rely on. I've
heard rumours that 'upvar' needs it, so I would want to hear from people who
have a use for its current behaviour, before deciding to "fix" it.

-Martin



reply via email to

[Prev in Thread] Current Thread [Next in Thread]