Inheritance and interfaces#


A fundamental idea in OOP is that you can make a more sophisticated object based on a simpler or more general object. For this we have derived or “child” classes. Notice the difference between an instance and a derived class. The instance also inherits from class, but it is fundamentally of the same nature as its sibling instances. A derived class is a new class that you can make instances of. They inherit the members of the base class (although the derived class’s code may overwrite base members), but cal also have additional features. Instances of a derived class are also instances of the base class.

Here’s an example:

:Class Person          ⍝ Person base class
    :field heightVal
    :field weightVal
    :field ageVal0

     Birth(h w)
      :Access public
      :Implements constructor
      (heightVal weightVal)h w

    :Property height,weight,age
    :Access public
         rGet x
     Grow cm
      :Access public

     Gain kg
      :Access public

     Lose kg
      :Access public

     Age y
      :Access public

    :property BMI
    :access public
:Class American: Person
    :field public ssn
     Birth(w h)
      :Access public
      :Implements constructor :base w h
      ssn1↓∊('-'@1¨⊢+¯1+?)1000 100 10000

So in the :Class header line, we have an additional colon (:) and the name of the base class. An American is really just another Person, but with a social security number. The social security number is given at birth, so we have a constructor that sets ssn. But we can’t just replace the constructor of the Person class, because it performs some important stuff too, namely initialising the weight and height.

Notice the :base in the constructor declaration. It tells APL to call the constructor of the base class. w h is used to propagate the constructor arguments to the base constructor. In this case, we wrote w h out for clarity, but it could also just have said Birth args :base args. APL would have made sure to find the right base constructor (for 2 arguments), and would have thrown an error if the user didn’t supply exactly two arguments.

Of course, you can also have a base class that doesn’t need any arguments to construct, but a derived class that does need arguments. In such a case, you’d have a monadic derived class constructor, with the line :Implements constructor :base. And, of course, you can have the opposite too, and differing number of args, etc. Mix and match as you see fit.

We can extend our classes further:

:Class NorthAmerican : Person

    :field public language'English'

     Birth args
      :Access public
      :Implements constructor :base args

:Class American : NorthAmerican

    :field public ssn

     Birth(w h)
      :Access public
      :Implements constructor :base w h
      ssn1↓∊('-'@1¨⊢+¯1+?)1000 100 10000

:Class Canadian : NorthAmerican

    :field public sin

     Birth(w h)
      :Access public
      :Implements constructor :base w h

:Class Swede : Person

    :field public pin
    :field public language'Swedish'

     Birth(w h)
      :Access public
      :Implements constructor :base w h


So here we have Americans and Canadians being derived from NorthAmerican which is a type of Person (yes, really). Each “level” adds its features to the final class’s instances.

If you deal with a lot of such derivations, you may want to know the hierarchy of a certain class or instance. Monadic ⎕CLASS gives you a vector of refs beginning with the class and ending with the most basic class. You may also want to know the opposite: which instances does this class have? Monadic ⎕INSTANCES gives you a vector of refs to all the instances of the given class.

c1 c2 c3{⎕NEW Canadian }¨(3 50)(4 55)(6 60)
(c1 c2 c3).⎕DF 'Albert' 'Bert' 'Charlie'
a1⎕NEW American (7.5 47)
a1.⎕DF 'Dave'
s1⎕NEW Swede (5 70)
s1.⎕DF 'Erik'
⎕INSTANCES NorthAmerican
│ #.Canadian │ #.NorthAmerican │ #.Person │
│ #.Swede │ #.Person │
 Albert  Bert  Charlie  Dave  Erik
 Albert  Bert  Charlie  Dave

There’s another nice system function when dealing with classes (and other scripted objects); ⎕SRC (SouRCe):

⎕SRC cl(⎕FIX':class cl' '∇r←SetDF x' ':access public shared' '⎕DF x' 'r←1' '∇' ':endclass').SetDF'yo!'
:class cl            
∇r←SetDF x           
:access public shared
⎕DF x                


A Dyalog interface is a script (unsurprisingly :Interface…:EndInterface) which defines some properties and/or methods. Then multiple classes can use a common skeleton framework. This can help ensure a harmonised API.

Consider, for example, the following:

:Interface FishBehaviour
 RSwim ⍝ Returns description of swimming capability

:EndInterface ⍝ FishBehaviour

Note that there isn’t any code in Swim. It is just a stub for the actual class to fill in. Interfaces can also have multiple such stubs:

:Interface BirdBehaviour
 RFly ⍝ Returns description of flying capability

 RLay ⍝ Returns description of egg-laying behaviour

 RSing ⍝ Returns description of bird-song

:EndInterface ⍝ BirdBehaviour

Now we can define a class with a base class, which implements these methods:

:Class Penguin: Animal,BirdBehaviour,FishBehaviour
      :Implements Method BirdBehaviour.Fly
      R'Although I am a bird, I cannot fly'
      :Implements Method BirdBehaviour.Lay
      R'I lay one egg every year'
      :Implements Method BirdBehaviour.Sing
      R'Croak, Croak!'
      :Implements Method FishBehaviour.Swim
      R'I can dive and swim like a fish'
:EndClass ⍝ Penguin

A derived class can only have a single base class, but you can use these interfaces to have something resembling multiple inheritance. Notice the :Class line. Animal is the base class, whereas methods and properties from BirdBehaviour and FishBehaviour are included in the Penguin class.