Looking for KornShell (ksh93) User Defined Type (typeset -T) documentation/examples

57 views Asked by At

A couple of months ago I came across Musings of an OS plumber. The one posting that really caught my attention was Using Types To Create Object Orientated Korn Shell 93 Scripts.

Ever since then I have been searching the Web looking for more information/examples of them. In all that time I've come to the conclusion that they are very rare because I have only found 3 examples and 3 documents (one of which is 'Type Variables' in the ksh93 man page).

If anybody here knows where I can find more on this feature then please can you pass it on to me. My plan is to write an article on this and post it here. I know of at least one person that would find it helpful.

1

There are 1 answers

7
user1683793 On

The ksh language is pretty neat and I'm glad somebody is taking an interest in the underused features. (Language?! Quite so, it is a fully functional and fairly powerful language that runs slow as... but that's another story.)

I'll give you this example and I'll fill in an explanation about what I am doing after.

#!/usr/local/bin/ksh
# This will do a show and tell using the typeset -T feature
# of ksh
# Sat Mar 30 01:01:35 AM EDT 2024
#
typeset -T TheTime_T=(
    typeset -S skew=0
    function get {
        now=$( date +%s )
        (( .sh.value=now+skew ))
        (( skew+=1 ))
    }
)
typeset -T Upper_T=(
    TheTime_T now
    typeset one=11
    typeset two=2U
    typeset countU=0
    typeset start="Upper"
    function initialize {
        typeset -S countS=0  # static
        typeset    countI=0  # instance
        (( _.countU+=1 ))
        (( countS+=1 ))
        countI=$(( countI+1 ))
        echo "init of Upper: ${!_}  S=${countS} I=${countI} U=${_.countU}"
    }
    function setStart {
        echo "Upper:setStart ${_.now} $@"
    }
    function endStart {
        echo "Upper:endStart ${_.now} $@"
    }
)
typeset -T Middle_T=(
    Upper_T _
    typeset middleVal="middle value"
    typeset start=middle
    typeset two="middle"
    function initialize {
        echo "init of Middle: ${!_}"
        .sh.type.Upper_T.initialize ${!_}
    }
    function endStart {
        echo "Middle:endStart $@"
        _.two="midEnd"
    }
)
typeset -T Lower_T=(
    Middle_T _
    typeset one=1L
    typeset start="lower"
    function initialize {
        echo "init of Lower: ${!_}"
        .sh.type.Upper_T.initialize ${!_}
    }
    function endStart {
        echo "Lower:endStart $@"
        echo "Ending the start process in mv=${_.middleVal} t=${_.two} ${_.one}"
    }
)

Upper_T uu
uu.initialize toStart
uu.setStart hownow
uu.endStart then

Middle_T mm
mm.initialize inMiddle
mm.setStart middleStart
mm.endStart middleStartThen

Lower_T ll=(
    middleVal="lower val"
)

ll.initialize TowardsEnd
ll.setStart startingLower
ll.endStart endingLower

When run with ksh2020 (which is better than I remembered) this resulted in:

$ ./tryT.sh
init of Upper: uu  S=1 I=1 U=1
Upper:setStart 1711826692 hownow
Upper:endStart 1711826693 then
init of Middle: mm
init of Upper: Upper_T  S=2 I=1 U=1
Upper:setStart 1711826694 middleStart
Middle:endStart middleStartThen
init of Lower: ll
init of Upper: Upper_T  S=3 I=1 U=2
Upper:setStart 1711826695 startingLower
Lower:endStart endingLower
Ending the start process in mv=lower val t=midEnd lower

I think it is explained in the man page that Name_T _ means inheritance. Class names start with caps and end with _T. You don't have to but it's a good convention. Variables that are prefixed by _. are class variables.

By way of explanation,
I created the TheTime_T class to give the idea of a get function. "At the office," we have a now built-in that gives Unix seconds since 1970 but for show and tell, I created this function that counts up each time it is called so we give the idea that time is progressing.

You can see the three classes Upper_T, Middle_T and Lower_T, each is based upon the one above, inheriting functions (aka, methods) and variables (properties). I'm not sure if it is in the man pages but _ is the current object so declaring _ to be of type Upper_T says that Middle_T is derived from Upper_T.

The Upper_T.initialize function has two count variables, one is declared with the -S, static. As always, static means shared between instances so the countS gets incremented for all instances countI only gets incremented for each instance. Why is countI incremented differently? I'll leave that as an exercise for the student (I don't know, that's what works).

A bit more about the syntax,

  • You can see _.two="midEnd". (I did not need the quotes but they don't hurt.) That sets the two variable in that Middle_T instance.

  • Observe the .sh.type.Upper_T.initialize ${!_} This is calling the initialize function in that class. There's should be a way to do it with something like _._.initialize but it does not work.

  • We see the echo of init of Upper: ${!_}. When this was run as a part of a variable of type Upper_T, this printed the variable name. When run when called directly from a lower class, it displayed the class name.

I put in a bunch of goofy variable manipulations that don't serve any purpose and are not particularly illustrative but maybe they will help getting the idea.

My colleague loved ksh to distraction and wrote massive programs with great complicated classes. I wish he had loved Python that much so the code would be written in a language that is actually controlled and generally agreed upon rather than just what happens to work.

Questions?