Open ormaaj opened 2 years ago
Here is a first attempt to backport it from ksh 93v- 2013-08-07. The changes all involve sh.oldnp
and np->nvenv
.
The patch makes your example script work:
typeset -a a=(zero one)
a[3]=three a[4]=four
printf '%s ' "${a[@]/~(E)^/${!__}=}"; echo
Output before patch: __=zero __=one __=three __=four
. Output after patch: a[0]=zero a[1]=one a[3]=three a[4]=four
.
However, the patch also causes a few regression test failures:
test pointtype begins at 2023-04-02+13:49:30
pointtype.sh[109]: FAIL: expansion of associative array of types is incorrect
pointtype.sh[111]: FAIL: typeset -p z for associative of types is incorrect
pointtype.sh[132]: FAIL: expansion of type containing indexed array of types is incorrect
test pointtype failed at 2023-04-02+13:49:30 with exit code 3 [ 36 tests 3 errors ]
test types begins at 2023-04-02+13:50:29
/usr/local/src/ksh93/ksh/src/cmd/ksh93/tests/types.sh[33]: Type_t: r: is read only
test types failed at 2023-04-02+13:50:29 with exit code 1 [ 100 tests 1 error ]
Of course I may well have missed something while backporting this, so please do verify the patch against the 93v- commit linked above.
There should also be a fix for it in ksh 93v- 2013-08-29 ("Fixed __ to work in nested types") but I haven't managed to isolated that from all the other changes in that commit yet. I suspect the fix involves an (ab)use of the NV_EXPORT attribute. It's so unfortunate they never kept changes in distinct commits :/
Having said all that, I'm not convinced this is worth integrating into ksh 93u+m. I don't think an interesting hack is worth it when it comes with "the usual assortment of bewildering bugs that obfuscate its intended purpose"; we prioritise fixing bugs here, so I'd like to avoid introducing any where possible.
In the example: printf '%s ' "${a[@]/~(E)^/${!__}=}"
(which, as of ceae1e44c8ffedb9dfc2fc4eb3432438991717f8 on the dev branch, can also be written as printf '%s ' "${a[@]/#/${!__}=}"
), the ${!__}
expansion does not have a constant value; it re-expands differently for each iteration of the vector expansion. This violates a basic principle of shell grammar, so I'm not a fan of that at all. There is a much cleaner way to achieve the same thing:
typeset -a a=(zero one)
a[3]=three a[4]=four
for i in "${!a[@]}"
do printf 'a[%d]=%s ' i "${a[i]}"
done; echo
…and that works on every ksh93, and makes for much more legible and understandable code.
As for the use in types, in the mailing list thread, you mentioned a workaround that you called "nasty": nameref this=${.sh.name%.*}; .sh.value=${this.y};
. Two questions:
__
hack?I could be convinced to backport __
, and try to fix it, if you can present an actual, concrete use case that is not feasible to implement in ksh as it is.
the
${!__}
expansion does not have a constant value; it re-expands differently for each iteration of the vector expansion. This violates a basic principle of shell grammar, so I'm not a fan of that at all.
…on the other hand, that turns out to be a pre-existing quirk of ksh, and not something introduced by this patch.
$ ksh -c 'echo ${@/~(E)^/$((++i))}' _ one two three four
1one 2two 3three 4four
whereas:
$ bash -c 'echo ${@/#/$((++i))}' _ one two three four
1one 1two 1three 1four
$ zsh -c 'echo ${@/#/$((++i))}' _ one two three four
1one 1two 1three 1four
I would consider the ksh93 behaviour to be clearly a bug, but there are probably scripts that depend on it. :-/
the
${!__}
expansion does not have a constant value; it re-expands differently for each iteration of the vector expansion. This violates a basic principle of shell grammar, so I'm not a fan of that at all.…on the other hand, that turns out to be a pre-existing quirk of ksh, and not something introduced by this patch.
$ ksh -c 'echo ${@/~(E)^/$((++i))}' _ one two three four 1one 2two 3three 4four
whereas:
$ bash -c 'echo ${@/#/$((++i))}' _ one two three four 1one 1two 1three 1four $ zsh -c 'echo ${@/#/$((++i))}' _ one two three four 1one 1two 1three 1four
I would consider the ksh93 behaviour to be clearly a bug, but there are probably scripts that depend on it. :-/
It's definitely a feature. The other shells just chose not to implement it for whatever reason. It's required in order to access sub-matches from .sh.match
that aren't accessible through ordinary group references, among other uses. There was at one time a bug in which the parameter expansions quit working and a fix was required to restore that behavior.
…and that works on every ksh93, and makes for much more legible and understandable code.
As for the use in types, in the mailing list thread, you mentioned a workaround that you called "nasty":
nameref this=${.sh.name%.*}; .sh.value=${this.y};
. Two questions:1. Why is that nasty? 2. Is it nastier than the 93v- `__` hack?
I could be convinced to backport
__
, and try to fix it, if you can present an actual, concrete use case that is not feasible to implement in ksh as it is.
I did not (and still don't) understand Irek and other's objections in that thread. I created that thread due to the misbehavior of _
and I'm pretty sure that must have been some oversight because I can't think of any reason for _
to not behave as I proposed (and DGK agreed and fixed it).
From that thread...
This can't work. .sh.value is identical to _
Is that even true. If so why? Why _
would need to be a synonym for .sh.value
.
Think about ksh93 types as compound variables with predefined discipline functions. In your example you're trying to assign the value of ++_.y to the value of a compound variable - which obviously cannot work.
I have no clue what was meant here. A type is nothing like a compound variable other than superficially borrowing bits of the compound assignment grammar for their definitions. Predefined discipline functions? What? I didn't even use a compound variable in that example and _.y
definitely doesn't refer to one there.
What about . to access the parent in both types and compound variable trees?
What on earth is a type tree? Why would a type have a parent? Compound variables sure.
I think the _
bug that was the topic there and the introduction of __
are totally separate issues. __
is only really interesting for its new functionality. It does make sense to have a way to access the current variable with _
in the case of compounds and the parent through __
. With regard to types I don't think it was ever discussed what role __
would have either as a member variable or in the context of a type's functions.
__
has many uses for accessing things that you can't get to any other way. I can't name them all because I almost certainly haven't discovered all that it does. In some contexts it behaves as a self-reference as you would expect_
to be used for, while in others it has a "parent-of-self" function.One interesting use is to access the current element during a replacement expansion:
Examples of other places where __ does "something":
.sh.math
function definitionstypeset -T
objects.sh
namespaceTo name a few. It also has the usual assortment of bewildering bugs that obfuscate its intended purpose.
The origin of
__
was a thread in which I pointed out that_
within a get/set discipline should reference the current object analogous to the way "properties" or "accessors" work in other languages. https://marc.info/?l=ast-users&m=137448362609866&w=4Evidently this usage conflicted with other people's ideas about what it should do, so
__
was added to refer to the parent object in contexts where hierarchical structures apply. It seems to have been extended for other uses, none of which are documented, of course.EDIT: Later in that thread I commented that
__
should work like python'ssuper()
or something. No clue what I was thinking there. That's nonsense unless ksh adopts a multiple inheritance scheme, which I wouldn't propose. :/