Advanced OO techniques#

Overriding methods#

Overridable methods then. Dyalog borrows this terminology from Visual Basic. In C# and Java, they are referred to as “virtual methods”.

If a derived class defines a method that has the same name as a base class method, then that shadows the base class method (although the base class method remains callable with ⎕BASE.MyMethod). However, if the derived class’ code calls a base class method which in turn calls a function by a name that has been defined both in the base class, and in the derived class, then it is the base class version that gets run. This is of course because the code that calls already is running in the base class. If in such a situation you want the derived class’ method to be called, then you need to override the base class method.

In order to do so, two conditions must be met:

  1. The base class method must declare itself to be overridable.

  2. The derived class method must declare that it is overriding the base class method.

Let’s look at an example. Here is a base class:

]dinput
:Class Base
     rO
      :Access Public Overridable
      r'O in Base'
    

     rF
      :Access Public
      r'F in Base'
    

     rCaller
      :Access Public
      rO F
    
:EndClass

We have three methods. The two single letter methods just report when they’re called. O says it is overridable, F doesn’t. Then there is a Caller method which just calls the two single-letter methods.

Here is the companion derived class:

]dinput
:Class Derived : Base
     rO
      :Access Public Override
      r'O in Derived'
    

     rF
      :Access Public
      r'F in Derived'
    
:EndClass

O overrides the base O, but F doesn’t. If we call Caller from (an instance of) Derived, it will of course execute in the base class, but since O has been overridden, it will call the O in Derived, while F will just call the F in Base.

I⎕NEW Derived
I.Caller
┌────────────┬─────────┐
│O in Derived│F in Base│
└────────────┴─────────┘

Keyed properties#

Let’s have a more in-depth look at properties, starting with keyed properties. Normally, indexing is for numbers only, e.g. vector[3 1 4] and matrix[3;1 4] etc. Sometimes you want an array-like thing where individual parts are identified by “keys” (usually character vectors).

For example, instead of referring to the individual columns of a database, you could refer to them by column name. Instead of having to look up each customer ID to find its current row in the database, you’d want to refer to the rows by “name”, e.g. the customer ID. Keyed properties allow you to do so, but of course, you have to write the look-up code below the covers, in the property’s code.

As you can imagine, the possibilities are endless, but here is a general keyed property skeleton which tells you what the APL code sees:

]dinput
:Class ClassK
    :Property Keyed K
    :Access public shared
         rGet x
          Show x
          
        

         Set x
          Show x
        
    :EndProperty
    Show{.({()}¨⎕NL-⍳9)}
:EndClass

You may remember the argument to the setter function from our first treatment of properties. It wasn’t very interesting then, but now it is of course critical. Also, note that the getter function now takes an argument. This is because we cannot just return the value of the property; we need to return the correct particular value using the keys.

For now, each function just calls Show which is a little, hacky, function that creates a visual representation of the argument; a two-column matrix of variable names and their values. The getter also has to force quit instead of actually returning something. This is to avoid having to generate some data which conforms to the shape of the request.

ClassK.K['Abe']2 5⎕A
ClassK.K['Abe' 'Bob']3 14
ClassK.K['Abe' 'Bob';'Name' 'Age']2 2'Abraham' 3  'Robert' 14
┌─────────────────┬───────┐
│Indexers         │┌─────┐│
│                 ││┌───┐││
│                 │││Abe│││
│                 ││└───┘││
│                 │└─────┘│
├─────────────────┼───────┤
│IndexersSpecified│1      │
├─────────────────┼───────┤
│Name             │K      │
├─────────────────┼───────┤
│NewValue         │┌─────┐│
│                 ││ABCDE││
│                 ││FGHIJ││
│                 │└─────┘│
└─────────────────┴───────┘
┌─────────────────┬───────────┐
│Indexers         │┌─────────┐│
│                 ││┌───┬───┐││
│                 │││Abe│Bob│││
│                 ││└───┴───┘││
│                 │└─────────┘│
├─────────────────┼───────────┤
│IndexersSpecified│1          │
├─────────────────┼───────────┤
│Name             │K          │
├─────────────────┼───────────┤
│NewValue         │3 14       │
└─────────────────┴───────────┘
┌─────────────────┬──────────────────────┐
│Indexers         │┌─────────┬──────────┐│
│                 ││┌───┬───┐│┌────┬───┐││
│                 │││Abe│Bob│││Name│Age│││
│                 ││└───┴───┘│└────┴───┘││
│                 │└─────────┴──────────┘│
├─────────────────┼──────────────────────┤
│IndexersSpecified│1 1                   │
├─────────────────┼──────────────────────┤
│Name             │K                     │
├─────────────────┼──────────────────────┤
│NewValue         │┌───────┬──┐          │
│                 ││Abraham│3 │          │
│                 │├───────┼──┤          │
│                 ││Robert │14│          │
│                 │└───────┴──┘          │
└─────────────────┴──────────────────────┘

Notice that keyed properties do not have any particular rank. The first two assignments treat K like it’s a vector, while the last one treats it as a matrix. APL does check that the indexers and the new values conform according to the rules of scalar extension.

Getting is exactly the same, except that the argument namespace does not have a NewValue member:

ClassK.K['Abe']
ClassK.K['Abe' 'Bob']
ClassK.K['Abe' 'Bob';'Name' 'Age']
┌─────────────────┬───────┐
│Indexers         │┌─────┐│
│                 ││┌───┐││
│                 │││Abe│││
│                 ││└───┘││
│                 │└─────┘│
├─────────────────┼───────┤
│IndexersSpecified│1      │
├─────────────────┼───────┤
│Name             │K      │
└─────────────────┴───────┘
┌─────────────────┬───────────┐
│Indexers         │┌─────────┐│
│                 ││┌───┬───┐││
│                 │││Abe│Bob│││
│                 ││└───┴───┘││
│                 │└─────────┘│
├─────────────────┼───────────┤
│IndexersSpecified│1          │
├─────────────────┼───────────┤
│Name             │K          │
└─────────────────┴───────────┘
┌─────────────────┬──────────────────────┐
│Indexers         │┌─────────┬──────────┐│
│                 ││┌───┬───┐│┌────┬───┐││
│                 │││Abe│Bob│││Name│Age│││
│                 ││└───┴───┘│└────┴───┘││
│                 │└─────────┴──────────┘│
├─────────────────┼──────────────────────┤
│IndexersSpecified│1 1                   │
├─────────────────┼──────────────────────┤
│Name             │K                     │
└─────────────────┴──────────────────────┘
]dinput
:Class Database
    :Field public DB0 3'' '' 0
    :Property Keyed K
    :Access public
         rGet x
          (id col)x.Indexers
          :If idDB[;1]
              rDB[DB[;1]id;'id' 'name' 'age'col]
          :Else
              ⎕SIGNAL 6 ⍝ value error
          :EndIf
        

         Set x;id;col
          (id col)x.Indexers
          :If idDB[;1]
              DB[DB[;1]id;'id' 'name' 'age'col]x.NewValue
          :Else
              DBid,x.NewValue
          :EndIf
        
    :EndProperty
    Show{.({()}¨⎕NL-⍳9)}
:EndClass
i⎕NEW Database
i.K['Dave';'name' 'age']'David' 31
i.K['Ernie';'name' 'age']'Ernie' 28
i.K['Dave';'name' 'age']
┌─────┬──┐
│David│31│
└─────┴──┘

Numbered properties#

A numbered property behaves like an array (conceptually a vector) which is only ever partially accessed and set (one element at a time) via indices. Here’s an example:

]dinput
:Class ClassN
    :Property Numbered N
    :Access public shared
         rGet x
          Show x
          
        

         Set x
          Show x
        

         rShape
          r2 3
        
    :EndProperty
    Show{.({()}¨⎕NL-⍳9)}
:EndClass
ClassN.N[1;2 3]'ab'
ClassN.N[1;2 3]
┌────────┬─────┐
│Indexers│┌───┐│
│        ││1 2││
│        │└───┘│
├────────┼─────┤
│Name    │N    │
├────────┼─────┤
│NewValue│a    │
└────────┴─────┘
┌────────┬─────┐
│Indexers│┌───┐│
│        ││1 3││
│        │└───┘│
├────────┼─────┤
│Name    │N    │
├────────┼─────┤
│NewValue│b    │
└────────┴─────┘
┌────────┬─────┐
│Indexers│┌───┐│
│        ││1 2││
│        │└───┘│
├────────┼─────┤
│Name    │N    │
└────────┴─────┘

It looks very much like our first keyed example, but there is an additional Shape function which allows APL to know what this imaginary array looks like. Also, note that the setter (and for that sake the getter) gets called once for each element that needs to be set (or retrieved).

Using this, you implement a sparse array in much the same way as we did the database. Basically, you’d make a 2-column table of indices and values, and then look up any requested index in the first column to find the corresponding value in the right column. When setting, we’d again look whether the index is already used, and then overwrite that, or if not found, add an entry to our “database”. This index lookup can be made very performant by means of a hashed array, 1500⌶.