ksh: Decide what to do about the unknown `getn` discipline

I just discovered that, contrary to all the documentation, ksh understands not four initial kinds of discipline functions, but five. There is an undocumented getn discipline: https://github.com/ksh93/ksh/blob/3f8add41482147bfa5595281c878953068e33c68/src/cmd/ksh93/data/variables.c#L111

I’ve experimentally found that the getn discipline is like get, but for arithmetic expressions. For instance, for $foo, the discipline function foo.get is called, but for $((foo)) (with no dollar sign before foo), the discipline function foo.getn is called (with no fallback to foo.get if foo.getn is not defined) (edit: yeah, that’s wrong, see further below). This is not documented anywhere and I cannot even find a single mention on Google.

$ foo.get() { echo GET; }
$ foo.getn() { echo GETN; }
$ : $foo
GET
$ : $((foo))
GETN
$ : $(($foo))
GET

A search in the ast-open-archive repo reveals that the getn discipline was quietly added in version 2009-08-21 93t+.

Annoyingly, there is no corresponding distinction for the set discipline. There is no setn discipline and the foo.set discipline function is called for both foo=bar and ((foo=1337)).

So, there are some things for the community to consider. Here are my views:

  1. I think it’s a bug that foo.get is not called for $((foo)). It’s contrary to all the documentation to date, which says that foo.get is called if the foo variable is referenced. Referencing it in an arithmetic expression without a preceding dollar sign is still referencing it.
  2. I think it’s wrong that $((foo)) and $(($foo)) call different disciplines. These expansions should be and are widely assumed to be equivalent. (See also 5da8eb31b32ced429017db534e5f6961c4cf7b7b)

So I think we should remove getn and make arithmetic expressions call get instead. I don’t think backwards compatibility is a major concern here as getn is completely undocumented and no one seems to have discovered it before me. It’s not even used in any of the other AST code in ast-open-archive, regression tests included. If anyone can find a trace of prior use, that might change things.

On the other hand, if we decide to keep and document getn, perhaps we should introduce a corresponding setn and make ksh call foo.setn instead of foo.set for $((foo)). But that would be a greater compatibility concern as the current behaviour of the set discipline is widely known and documented.

Or we could change nothing and just document the current situation. It does not seem to be possible to introduce consistency without introducing some incompatibility or another.

I tend to put a high priority on consistency, so all things considered, removing getn and folding it into get seems to be the lesser evil to me.

Thoughts, opinions?

About this issue

Commits related to this issue

Most upvoted comments

Considering that:

  • this is yet another undocumented, unfinished, forgotten about and abandoned experiment (the planned setn discipline was never implemented),
  • it’s crashy,
  • the intermittent crashes are not realistically solvable (at least not by me),
  • virtually no one even knows about this, and
  • this is taking too much time and effort for too little benefit,

I now think it’s best to just get rid of the whole thing.

I’m also not actually convinced this is the best design. Defining additional disciplines will confuse most users and a correct usage in scripts will be the exception rather than the rule.

The problem it was meant to solve could be perhaps be solved in a different way. In the normal get and set disciplines, .sh.value could inherit any integer/float typeset attributes of the parent variable, which would fix the string conversion problem. But that’s a project for a post-1.0 release.

The following thread on the old mailing list seems relevant: https://www.mail-archive.com/ast-users@research.att.com/msg00601.html