Objects and classes#

Next up is a special case of a namespace called a class. Remember: All APL objects are namespaces. The ones we just call “namespaces” are the most general ones with no restrictive rules. Classes can hide stuff from the outside onlooker. Adhering to a set of rules, they can be used to create other objects (instances). All this should be familiar to you if you’ve done any OOP (object oriented programming), e.g. in C#, Java or Python.

Remember that we can tell the editor to begin a new namespace with )ed ⍟myns? We can begin editing a new class with )ed ○myclass. We could also create a new empty namespace with ⎕NS. We can’t do that with classes as they need some meta-information.

Fundamental to a class it that it restricts which of its members can be “seen” from the outside. By default fields (i.e. variables) and methods (i.e. functions) are “private”, but we can make them “public” so that they can be seen. This is convenient to implement black-box things and create layers of abstraction (for those that like such). Another feature of fields and methods is whether they are “shared” among all the instances, or whether a separate method/field belongs to each instance. By default, they belong to the instances.

So what is an instance?

An instance is a new object which is based on a class, which is then its base class. Instances inherit all methods and fields from their base class, but they may either each have their own or share one (which is then considered as if it remains in the base class).

Let’s see some code:

]dinput
:Class cl
    :field f'f'
    :field public fp'fp'
    :field shared fs'fs'
    :field public shared fps'fps'
     rlook
      :Access public shared
      r⎕NL -⍳9
    
:EndClass

The above is a script for a class called cl. You can see that it has four fields and one method (function). The first field, f, has all the defaults, i.e. it is private, and for each instance. The second field, fp can be seen from outside each instance. The third, fs, is private, but shared among all instances (and their base class, cl). The last field, fps is both visible to the outside public, and also shared. The method, look, is public and shared, just like the field fps.

So, if, from outside cl, we try looking into cl, which members can we see? We won’t be able to see fp because it is instance, not shared. So since cl is not an instance, it won’t show fp. We can verify this:

cl.⎕NL -⍳9
┌───┬────┐
│fps│look│
└───┴────┘

Now, let’s step into cl and have a look from inside. We do that by running cl.look. As you can see, look just returns the list of members that it can see.

cl.look
┌──┬───┬──┬────┐
│cl│fps│fs│look│
└──┴───┴──┴────┘

Note that cl is in there, just like a function can “see” itself:

f{⎕NL-⍳9}
f 
┌──┬─┐
│cl│f│
└──┴─┘

Everything that’s shared (i.e. non-“instance”) can be seen, and also the class itself. This is useful if you work with a class and need to inspect what’s going on inside. You can just trace into any public function, and then leave the system suspended. Now you can work from inside the class. When you’re done, just execute →0 to quit the function.

Let’s try to create our first instance of cl. We do that using the system function ⎕NEW. It takes cl as right argument and returns an instance:

inst⎕NEW cl
inst.⎕NL -⍳9 
┌──┬───┬────┐
│fp│fps│look│
└──┴───┴────┘

If you expected fs, then it is shared alright, but remember that we’re on the outside. It isn’t public. We can see look, because it is public (and shared too, but that doesn’t matter here). We can also see fp which we couldn’t see before, because it is an instance field. But now we do have an instance, and as it is public too, we can see it.

Now, let’s run inst.look. What do we get?

inst.look
┌──┬───┬──┬────┐
│cl│fps│fs│look│
└──┴───┴──┴────┘

The reason look cannot see f is because f isn’t public. But we’re inside, you say? Yes, but inside what? Remember that look is a shared method. This means that it resides in cl, not in inst. And from inside cl, the private fields of inst are invisible. To prove this, we can make a small modification to the class:

]dinput
:Class cl
    :field f'f'
    :field public fp'fp'
    :field shared fs'fs'
    :field public shared fps'fps'
     rlook
      :Access public                  ⍝ Note: no longer 'shared'!
      r⎕NL -⍳9
    
:EndClass
inst.look
┌──┬─┬──┬───┬──┬────┐
│cl│f│fp│fps│fs│look│
└──┴─┴──┴───┴──┴────┘

The only difference here is that look is now an instance method. This means that we can no longer do cl.look.