Conversation started Feb 27, 2018 at 18:30.
Feb 27, 2018 6:30 PM
Welcome to APL Cultivation!
We're going through classes as part of object orientated APL.
@Adám Except, as far as I can tell for the time being, iterators and generators.
Today, we'll actually begin to do stuff, using properties.
@Pavel This week, I'm working from home and John D (our OO guy) is in Italy. I have not forgotten. I'll ask him next week.
So far, classes have acted pretty much like restricted namespaces. Properties act much like fields/variables, but allow us to take special action when they are set or used.
Have a look at this code:
:Class Person

    :Field public name←'-'

    Upper←1∘(819⌶)
    Lower←0∘(819⌶)

    :Property Name
    :Access Public
        ∇ text←Get
          :If '-'≡name
              text←'I don''t have a name!'
          :Else
              text←'Hi, my name is ',name,'!'
          :EndIf
        ∇
        ∇ Set text
          name←(Upper 1↑text.NewValue),(Lower 1↓text.NewValue)
        ∇
    :EndProperty

:EndClass
You already know about fields from lesson 17.
Upper and Lower are two functions (methods) which just uppercase and lowercase.
Then we have a block which defines the property Name. It doesn't matter that it only has casing difference from the name field, but it is convenient to remember their connection.
The way properties work is that they have 1–3 specially named functions. Here, Name has Set and Get.
@Adám so Name ≠ name ?
@J.Sallé Correct.
Only system functions and keywords are case insensitive (user and system commands are too, but they're not really code). Identifiers are case sensitive.
makes sense
Feb 27, 2018 6:38 PM
The Get and Set functions have to be named thus, but you may case them as you want, to fit with whatever coding style you choose.
@Adám What's the 3rd function?
@Pavel The third one is called Shape, but it only applies to a special kind of properties which I don't think we'll have time to cover today.
Ok
So, Name will be treated as a public (due to the :Access declaration) field, but instead of directly setting a variable, the Set function will be called whenever one uses assignment syntax for Name.
However, Set doesn't just get the new value as argument. Rather, it gets a namespace with some members (you'll see later why). The important member here is NewValue, as you can see.
Can I have public Get and private Set?
Feb 27, 2018 6:42 PM
Get is called when one attempts to use the value of Name.
@Adám just to clarify, I couldn't just do Person1.Name←'name'?
You could, that's the point
@Pavel I'm not sure that even makes sense. If you can't "see" Name from the outside, then you'd be overwriting it if you attempt to set it.
@J.Sallé Yes you can. But under the covers, that'll call Set
@Adám That means you can see the value from outside, but only modify it from inside.
@Adám okay, I imagined such
Feb 27, 2018 6:45 PM
@Pavel You may omit one of Get or Set.
@Adám But I do still want to be able to modify it from inside.
@Pavel Sure, but from inside, you can call and assign to whatever you want. You may have more functions inside a property block, which you can then use to handle setting, etc.
@Pavel And maybe you can. I tried experimenting, but it didn't work out the way I expected, so I may have to take some time or ask about it.
Ok. If this was C#, our Person class might have a public int Age {get; private set;}. From the outside, I can only see Age's value, not tamper with it. From the inside, I could do Age++ at regular intervals.
@Pavel Right, I'm very sure you can do that here too. I just didn't take time to prepare an example of that for this lesson. I'll put it on my to-do list.
Thanks!
{get; private set;} is a very common pattern I'm used to.
Feb 27, 2018 6:50 PM
@Pavel Yeah. There are really a lot more details to all these things that what I cover in the lessons. E.g. you can declare members ReadOnly. But now we're getting off-track.
So, just to explain the actual code. :Field initialises the name to be a dash. Get will check whether name is a dash or not, and respond accordingly. Set will accept a character vector and make sure the casing is right (upper initial, rest lower) before assigning to name.
@all Clear?
perfectly
OK. Now of course, a class can have more than one property. Let's have a look at a fancier version:
:Class Person

    :Field age←0
    :Field name←'-'

    :Property Age
    :Access Public
        ∇ num←get
          num←⌊age
        ∇
    :EndProperty

    ∇ Grow amount
      :Access Public
      age+←amount
    ∇

    Upper←1∘(819⌶)
    Lower←0∘(819⌶)

    :Property Default Name
    :Access Public
        ∇ text←Get
          :If '-'≡name
              text←'I don''t have a name!'
          :Else
              text←'Hi, my name is ',name,'!'
          :EndIf
        ∇
        ∇ Set text
          name←(Upper⊃text.NewValue),Lower 1↓text.NewValue
So I've added two things. The most obvious one is the Age property and the complementary method Grow. That should all be understandable to you by now. If it isn't, please interrupt.
What does :Property Default mean?
@Pavel Right, that's the other change. Well spotted.
What's Lage?
Feb 27, 2018 6:59 PM
I think thats [Floor]age
@Hosch250 Floor of age
@Hosch250 lol that's not L
...I'm a bit late
APL ninjas.
OK. Too used to UTF-8.
Feb 27, 2018 7:01 PM
Normally, objects are passed by reference while arrays are passed by value. But the monadic called Materialise has the ability to transform references into values. So if a method has a default property, then monadic will yield this property.
@Hosch250 ...*un* used, you mean, right? or you're feeling a bit dizzy?
dah why markdown why
Try it online:
https://tio.run/##hVJNT8JAEL3zK8bTQiwNxMQoN1REElOJBj2vdKlNtrubfoicTRAJISbGxKt/Qv/P/pG6u22xoIY5bd/Me9N5M1jQujvBlHtp2jqmOIqgT8KIs0oFVLROfUJdwB6R05dGGWI40Biqo7yyH3JBwngCbY9kSHs4JFovuaX@0EA65OwJWBIorkfiFQo5JhdznPPz4kyrw9yiQdZPy3RDPgYc8IQVQn/2VIK7SrpUqGXNYyCUpMo15ey9etA8lIvPmkmc87FJNMqJjUFPyAgnNAZHWbFt4pg8xEquuzZyqzcCZaB8/mCFxE/kDNQDlzOEYrjD9wSwMX4HlVU6NPqPfOZbEEwMB/wIkKVfFtrgM7c3@mV58edXJDZy5VVlu69m7s0fddp2yPga04TULOMdNOX0dS2xZan6yxxgmgp9CMs3p3NTHKOwnfzeji4HzgVSgD4zYZsT2LP314HGCpCLL/EN
wat
They're all UTF-8 characters
Yes, just dizzy.
Take your time to go to TIO and play with that one until it is clear.
Feb 27, 2018 7:04 PM
@Adám only one property can be set as default, I assume?
Also, is Default also case sensitive?
@J.Sallé Yes. That's the whole point of a default property. However, it may be an array, of course.
@J.Sallé No
@J.Sallé No, all those sub-keywords, like default public instance etc. are all insensitive.
@J.Sallé only identifiers are case-sensiitive :P
ninja again
Okay, got it.
Feb 27, 2018 7:05 PM
@EriktheOutgolfer Well, it just shows that you all pay attention. Makes me happy.
@Adám actually I only pay half the attention, but I don't have any issues :P
Now we're at it, I should mention that monadic on .NET collections materialises the collection's items: It returns an array of the .NET items that the collection consisted of.
(the tab title helps a bit)
You can of course make your class have that same behaviour by setting the default property appropriately.
What about on other kinds of .NET objects? I'm guessing on a System.String it makes it a character vector, but what about something miscellaneous like a System.DateTime?
Feb 27, 2018 7:10 PM
So, if you have a default property, you can read/write to the class instance directly, and it uses that property?
@Pavel Yes .NET strings become character vectors, but things that cannot be broken down (like DateTime) just stay the way they are.
@Hosch250 Only read. instance← would overwrite the name.
@Pavel OK, finally I'm going to answer your question.
Yay
The normal display of an object is with a namespace path and object name or class name/"namespace" in brackets. Not very useful.
⍞←⎕NS ⍬
Feb 27, 2018 7:13 PM
@Adám #.[Namespace].[Namespace]
However, there the system function ⎕DF (Display Form) allows you to change this to any character array:
⎕←ns←⎕NS ⍬ ⋄ ns.⎕DF 2 2⍴'yo' ⋄ ⎕←ns
@Adám
#.[Namespace].[Namespace]
Well that didn't quite work
@Pavel Nope. Try it online instead.
Of course, having a static display form like that isn't much fun.
Here is a better usage:
:Class Person

    ∇ Birth
        :Implements constructor
        :Access public
        ⎕DF 'baby'
    ∇

    Upper←1∘(819⌶)
    Lower←0∘(819⌶)

    :Property Name
    :Access Public
        ∇ text←Get
          :If 0=⎕NC'name'
              text←'I don''t have a name!'
          :Else
              text←'Hi, my name is ',name,'!'
          :EndIf
        ∇
        ∇ Set text
          name←(Upper⊃text.NewValue),Lower 1↓text.NewValue
          ⎕DF name
        ∇
    :EndProperty

:EndClass
So now we have a constructor which sets up the initial display form. And every time the Name property is Set, the display form is updated.
Can I make it so ⎕DF of an object isn't callable from the outside?
Feb 27, 2018 7:20 PM
@Pavel Hm, I don't think so. You want to prevent users from changing it?
Yeah
@Pavel That sure makes sense, and comes back to a question I've had for a long time: Why is ⎕DF a function rather than a variable. If it was a variable you could conceivably make it a readonly field.
Well, you could define a default property that printed your custom text?
@Hosch250 Yes, but that wouldn't show just when entering the object name. It requires to show up.
Oh, I see.
Feb 27, 2018 7:23 PM
@Pavel I've added it to my list of questions for JD.
I forgot to put ^ . Sorry.
Now, as you know, objects are passed by reference. So if we just try to grab the object value, we get a ref rather than the display form, even if the display form is what shows in the session. How do we get the actual display form? In C# it would be ToString of course.
Quiz: Any guesses?
Niladic ⎕DF?
@Pavel no way, niladic?
@Pavel Nope, since that's a monadic function, you can't call it niladically; you'd just grab the function itself (e.g. to use with an operator).
Think about it: If you have a numeric array, how would you get the character array display form?
@Adám but what if we want the actual value to not be a character vector?
@EriktheOutgolfer What actual value?
Feb 27, 2018 7:31 PM
@Adám like, not the reference, but the value that is where the reference points to
@EriktheOutgolfer There is no difference. Well, for a namespace, you could conceivably want a deep copy, but for classes? You want a new fresh instance?
is APL's ToString. So ⍕object will give you whatever argument has been fed to ⎕DF.
I don't think you can dereference classes like that in APL.
@Pavel What would it even mean? The memory location? That isn't useful in APL.
No, the other way around. Instead of having a pointer to an object, you have the actual data representing the object.
@Pavel As a name-value pairs' list? A JSON representation?
Feb 27, 2018 7:35 PM
No. As the raw representation. In APL an object is a pointer, right? Has a size of 4 bytes? Dereferencing it would yield the actual object in memory. It might be more or less than 4 bytes depending on the struct itself.
This wouldn't be a useful operation, I think.
@Pavel I agree.
But, since we're at it anyway, I think I mentioned that you can create a deep copy (so not a ref) of a namespace (including a class) with ⎕NS.
Another cool thing you can do is overtaking:
Remember how APL pads with the a fill element if there are not enough elements to go?
⍞←10↑3 1 4
@Adám 3 1 4 0 0 0 0 0 0 0
If a class has a niladic constructor, then overtaking an instance will create siblings (i.e. new instances of the same class) using the niladic constructor: Try it online!
Very similar to C++.
So, does Dyalog have null?
@Hosch250 Yes, ⎕NULL.
Feb 27, 2018 7:42 PM
OK.
@Hosch250 It isn't used much though. I usually see it representing empty cells in spreadsheets and occasionally as placeholder value for a ref.
Good.
I'm looking forward to no-null in C# 8.
Ikr
One thing that I use quite often is selecting cells in Excel, copying to clipboard, and then in APL, I place the cursor on a name and click "Paste as object". That makes the name hold a matrix of the speadsheet cells. Numbers are numbers, texts are character vectors, and empty cells are ⎕NULLs.
@Adám once again the APL implementation shows clear predilection for sorcery and wizardry.
Feb 27, 2018 7:49 PM
@J.Sallé ...or just the ability to parse the clipboard contents :P
with the Paste button passing the clipboard contents to some classification function
@EriktheOutgolfer Sorcery, I tell thee
Obviously, I cannot cover everything in these lessons, but here are a couple of things that properties can do as well:
You can have a :property numbered which acts like a normal property, but if you use indices to set or get, those functions are called with a namespace that has an Indexers member to tell the function which elements are being asked for.
Remember that I mentioned the Shape function of a property? This of course means that a property can have any (pretend) shape. So when Get or Set are called, the argument has a member called IndexersSpecified which is a Boolean vector indicating which dimensions are being addressed.
You can use this e.g. to implement sparse arrays.
The one and only APL cultivation that lined up with my schedule flawlessly: fire alarm goes off, school evacuates.
@Adám so, assuming :property numbered Grades, then I could do Student.Grades[1]←'A'⋄Student.Grades[2]←'B'?
@J.Sallé Yes, and even Student.Grades←'AB'.
Feb 27, 2018 7:59 PM
@Adám ah, okay. If there's already some information on that property, it'll just keep adding more indices whenever I call the set method again?
You can also have a :Property keyed which instead of numeric indices can use any arrays as keys. It is then up to the Set and Get functions to handle. Typically you'd want to use character vectors as keys. For such properties you must use indexing, as APL cannot know how many "elements" there are.
You can use this to implement dictionary objects.
@J.Sallé Well, you must have a Shape function for numbered properties to tell APL how many elements there are, but of course, that function can give a dynamic result.
@Adám I see
Thank you so much for participating!
 
Conversation ended Feb 27, 2018 at 20:02.