Conversation started Feb 21, 2018 at 18:30.
Feb 21, 2018 18:30
Welcome to APL Cultivation!
Last time we introduced classes. Now we will learn how to actually use them.
When you create an instance of a class using ⎕NEW BaseClass, you may want to supply some parameters.
For this, we use a special type of method (function) called a constructor.
When you create a new instance, a constructor (if one exists in the class script) will be called.
:Class cl1

    ∇ Ctor
      :Access public
      :Implements constructor
      ⎕←'Hi!'
    ∇

:EndClass
We define a tradfn called Ctor (it could be called anything, though) and declare it available from the outside (it must be, as you can't be inside yet when you're just creating the instance).
@Adám That looks verbose. Is there a... golfier way of doing it? :-)
Then the :Implements line declares it a constructor.
@wizzwizz4 Only removing white space and using shorter names. APL is not a golfing language.
The line⎕←'Hi!' is the actual code in the function. It is then executed every time ⎕NEW runs, as you can see in TIO.
@Adám Can you set a constructor dynamically?
@wizzwizz4 What does that mean? You take a non-constructor method and make it into a constructor later?
@Adám Or a function and add it as a constructor.
Feb 21, 2018 18:38
@wizzwizz4 Well, you can always edit the class script, and all the existing instances will update (this is unique to Dyalog APL, I think), but since a constructor only runs at creation time and never in the base class, it will not run if it was added later. However, any new instances created after the edit will run it.
lol TL;DR make your constructors right away
@Adám That's a neat feature. Is that due to lack of caching?
@wizzwizz4 I'm not sure what you mean by lack of caching. I know that it was a pain to implement.
Now have a look at this modified class:
:Class cl2

    :Field public value

    ∇ Ctor x
      :Access public
      :Implements constructor
      value←x
    ∇

:EndClass
@Adám Oh, wow. If that was deliberate, it must have taken some work. I assumed it was a side-effect of the implementation.
Here we have an uninitialised field (variable) and a monadic constructor which sets the variable upon construction: Try it online!
@wizzwizz4 It was really complicated to get to work. Indeed, base classes are "cached", because i you delete the base class after making an instance, an unaccessible "phantom" base class is kept until the last instance is gone.
Now:
:Class cl3

    ∇ None
      :Access public
      :Implements constructor
      ⎕←'No arguments.'
    ∇


    ∇ One x
      :Access public
      :Implements constructor
      ⎕←'1 argument:'x
    ∇

:EndClass
Here is a class with two constructors. APL will call the appropriate one (niladic or monadic): Try it online!
Feb 21, 2018 18:48
Can I set something like C#'s ToString so we can print the output in a prettier way?
@Adám This is "overloading"?
I don't think so. The methods have different names.
@Pavel Yes, yes, but we're not there yet.
@wizzwizz4 Feel free to call it that, if you so wish. It is what it is.
@Pavel Open notepad or leafpad or something and write questions there. That way, you can ask them when we get there.
@Adám looks like you can't have more than 1 argument, can you?
Feb 21, 2018 18:50
@EriktheOutgolfer I can only type so fast…
:Class cl4

    ∇ None
      :Access public
      :Implements constructor
      ⎕←'No arguments.'
    ∇


    ∇ One x
      :Access public
      :Implements constructor
      ⎕←'1 argument:'x
    ∇

    ∇ Two(a b)
      :Access public
      :Implements constructor
      ⎕←'2 arguments'a b

    ∇

:EndClass
@wizzwizz4 Or, in my case, kate.
Any guesses?
@Adám sorry, but I can't know what you're going to post next :)
@Adám Zero, one and two?
@wizzwizz4 Yes, and APL will call the appropriate one: Try it online!
Feb 21, 2018 18:51
@Adám Can you have typed overloading?
@Adám I tested it, and the result isn't good...
Or is it more like Python in that respect (ignoring types in syntax)?
@EriktheOutgolfer You need to be more precise with what you mean. Do you mean that different things happen when you ⎕NEW depending on the type of the argument?
@Adám i.e. the result is an error
@Adám Yes.
Feb 21, 2018 18:53
@EriktheOutgolfer Where, doesn't my TIO link work?
(Is that a reply to the wrong message?)
@wizzwizz4 Hang on.
@Adám your TIO what? there's no link for cl4, and this makes it look like it will result in an error
oh
oh you need parentheses?
@EriktheOutgolfer It's in the response to wizzwizz4.
@Adám yeah I had skipped it
Feb 21, 2018 18:55
@EriktheOutgolfer Yes, ⎕NEW takes one or two arguments. The second (optional) argument are the constructor args.
@Adám uh, maybe it should've been ⎕NEW's left argument instead, if it's not already something else...
@EriktheOutgolfer It tries to follow C# syntax.
:Class cl5

    :Field public name←'baby'
    :Field public age←0

    ∇ Ctor x
      :Access public
      :Implements constructor
      :If ' '=⊃0⍴x
          name←x
      :Else
          age←x
      :EndIf
    ∇

:EndClass
@Adám yeah, you can't reply with a code block
@wizzwizz4 ^^ Has a single constructor which decides what to do. Try it online!
@EriktheOutgolfer I think you can if it's quoted (> ). That doesn't exist.
Feb 21, 2018 18:59
@wizzwizz4 quoting is something different ;)
@Adám I expected that to be possible... I was wondering whether the language would do it for you.
@wizzwizz4 So the multiple constructors can take care of number of arguments, but you can of course also handle everything in a single fancy constructor.
@wizzwizz4 No, in APL, all arrays are created equal ;-)
@Adám Harder to read though, often.
@Adám Are functions first-class objects? (I think yes, just checking.)
@wizzwizz4 I had to type it up fast. I'd put in some comments if I was to do this IRL.
@wizzwizz4 Yes. Check previous lessons on operators.
Not entirely first-class, i.e you can't have an array of functions
Feb 21, 2018 19:01
@Adám hm, that's not what I usually think as "first class"
@H.PWiz There are actually two ways to do that. But not today's lesson.
@Adám I would love to see it. Can you jot reduce one? (let's move on)
@Adám If you don't put limitations like that in place, we'll completely derail it in seconds. :-)
@Adám Store an array of System.Funcs. :P
Feb 21, 2018 19:04
OK, final example of multiple constructors:
:Class cl6

    ∇ None
      :Access public
      :Implements constructor
      ⎕←'No arguments.'
    ∇


    ∇ One x
      :Access public
      :Implements constructor
      ⎕←'1 argument:'x
    ∇


    ∇ Two(a b)
      :Access public
      :Implements constructor
      ⎕←'2 arguments'a b

    ∇

    ∇ Three(a b c)
      :Access public
      :Implements constructor
      ⎕←'3 arguments:'a b c
    ∇

:EndClass
We have 0–3. What happens when I call this with more than 3?
@Adám One is called with an array?
@Adám Varargs of some description?
@dzaima Yup. Nice: Try it online!
@wizzwizz4 The one with just a single argument is effectively a "varargs".
@Adám Can you set a different one as varargs?
e.g. to distinguish between... can't remember the syntax but ((1 2 3 4 5 6 7 8)) and (1 2 3 4 5 6 7 8)?
@wizzwizz4 You probably mean ⊂1 2 3 4 5 6 7 8 and 1 2 3 4 5 6 7 8.
@wizzwizz4 No, the a single named argument indicates that it can take a variable number of args. If you want to refuse construction with non-scalar arg the constructor will have to inspect the arg and throw an error.
OK, enough about constructors. Lets talk about something fun: Destructors.
Feb 21, 2018 19:11
@Adám Will the error then be interpreted as an invalid call?
Oops.
@wizzwizz4 oops what?
@wizzwizz4 You can throw any error you want with any message you want, so yes, you can throw a length error.
@Adám How much violence are we talking?
@EriktheOutgolfer We've moved on.
@wizzwizz4 Not a problem. I should have prompted for questions. I just assume you all will interrupt me. (Which is what I want!)
Are APL Destructors basically C++ Destructors?
Feb 21, 2018 19:13
@Adám Ok. If the constructor throws an error, will it be recalled with the variable argument format?
Sometimes when an instance ceases to exist, you want to do some clean-up. E.g. when the webserver is closed, you want to free ports and write a message to the log, etc.
@Pavel s/++/#/?
@wizzwizz4 Recalled?
@Adám *re-called
E.g. if Two throws, will One be called with an array of two?
I did mean C++, but it sounds like they actually are like C#'s Finalize.
Feb 21, 2018 19:14
@wizzwizz4 No.
Ok. So, no special errors.
@Pavel I don't know any C++, but maybe you'll be able to tell in a little bit.
:Class cl7

    ∇ Hi
      :Access public
      :Implements constructor
      ⎕←'Hello there!'
    ∇

    ∇ Bye
      :Implements destructor
      ⎕←'See you later!'
    ∇

:EndClass
Do I need to explain?
In C#, when the garbage collector deletes an object, it calls its Finalize method. You can override it to do cleanup.
@Pavel Ah, but in Dyalog APL you can call most methods what you want. Hence the need to declare it a destructor.
@Adám Can you have more than one?
Feb 21, 2018 19:18
@wizzwizz4 No, because ceasing to exist doesn't have any arguments. Should be obvious, no? I suppose you could have a method that takes arguments, does something with them, and then kills the self somehow.
@Adám It is... but what about subclass destructors?
How would they work if you can only have one?
Is there an equivalent of Python's super?
@wizzwizz4 Ah, each level can have a destructor and they will all be called. That's fine.
@wizzwizz4 What does that do/what is that?
@Adám In Python magic methods are named, e.g. __del__. super().__del__ is a reference to the parent's __del__, going up the MRO (method resolution order, a solution to multiple inheritance) to invoke the closest version. Otherwise the parent class destructors aren't implicitly called if overridden.
How do you stop a parent's destructor from running?
For example, if you subclass your webserver so it shares a socket (like SATA's master/slave).
@wizzwizz4 You can call base class methods with ⎕BASE.MethodName.
@Adám Interesting.
Feb 21, 2018 19:23
@wizzwizz4 IFAIK, you can't.
What if you don't know the name, but you know its implementee?
@wizzwizz4 why wouldn't you know its name >_>
@wizzwizz4 If something is shared, then it it won't go away when the instance dies. And of course, the instance destructor shouldn't just close the socket. Rather it should check whether there are any others that use it first.
@EriktheOutgolfer If you're using somebody else's code and they're terrible at keeping a consistent API?
@wizzwizz4 The name of what? And what is an implementee?
Feb 21, 2018 19:25
@wizzwizz4 uh, then you shouldn't use their code, simple ;)
@Adám Sorry. The thing it implements, e.g. destructor.
@EriktheOutgolfer That's often not a choice in Serious Business Programming™.
@wizzwizz4 You shouldn't (and can't) call the base (or any) destructor. I don't understand what you're getting at.
@Adám Trying to do something I shouldn't. Never mind.
Wait... if we can't call it, why does it have a name?
@wizzwizz4 Although I've previously pondered why you can't kill an instance by calling its destructor. That way, you could have destructors with arguments… Anyway, I'm only teaching what Dyalog APL currently offers…
@Adám That might not have been a possibility that entered the original developer's mind. But it should be easy enough to replace the socket itself in that scenario so it didn't close when bidden.
@Adám Because then APL would have to deal with null.
Feb 21, 2018 19:29
@wizzwizz4 I guess it technically doesn't need to have one, but for sanity and the ability to find it in the class tree-view.
@Adám Can you reuse its name?
@wizzwizz4 Null?
@Adám Well, technically NULL...
If you've destructed an object, you're left with references to...
Garbage data?
An unresponsive object?
@wizzwizz4 Re-use the name of the destructor? I guess you could shadow it and then reuse it, but I wouldn't recommend it.
@wizzwizz4 The destructor won't run until the last ref is eliminated.
@Adám Would anything particularly bad happen, apart from the inward groaning of anybody looking at the code?
Feb 21, 2018 19:31
(as if like that's not bad enough)
@Adám You've answered your pondering, I think.
Unless you're willing to deal with that, destructors shouldn't be callable.
@wizzwizz4 I don't think so. But really, APL allows arbitrary length names and gives you three alphabets (upper, lower, circled/underlined)…
I didn't really expect them to be except by a technicality, which would cause Bad Things™ to happen.
@wizzwizz4 Ah, I see what you mean now. But ⎕EXing an instance has the same problem. Calling the destructor with an argument could just keep that argument on storage for later.
@Adám Then what happens if it's called again?
Also, you're not really dealing with destructors any more.
That's a queue.
It doesn't belong in destructor-handling code.
Feb 21, 2018 19:35
@wizzwizz4 Right. Let's put that idea aside.
You can shadow the destructor if you like... :-)
@EriktheOutgolfer Bad enough yet? :-p
@all Any other questions about constructors and destructors? Or other things about classes that we've covered so far?
@Adám is calling a class's destructor from within the instance intended to throw a VALUE ERROR?
@Adám Are there true primitives, or are those instances of classes?
@EriktheOutgolfer It surely should throw some kind of error.
Feb 21, 2018 19:39
@EriktheOutgolfer I don't think there's a WHY? ERROR.
:Class ICallDestructorsImLame

    ∇ Destruct
      :Implements destructor
    ∇

    Destruct

:EndClass
inst←⎕NEW ICallDestructorsImLame
@wizzwizz4 What APLers call primitives are the built-in functions and operators. But APL isn't OO in itself.
those two together raise a VALUE ERROR
@Adám
@EriktheOutgolfer What happens if you call something unnamed?
@wizzwizz4 like what?
Feb 21, 2018 19:41
@EriktheOutgolfer the Destruct should be in a function
@EriktheOutgolfer It sure does make sense, doesn't it? APL gives value errors for things you don't have access to, e.g. trying to call a public method from outside, or an instance method from of base class..
@EriktheOutgolfer Trying to call Distressed. My theory is that the error will be the same.
If I understand the language feature.
@dzaima Doesn't make a difference: Try it online!
@wizzwizz4 uh, yes? although that's not what I call "something unnamed" but rather "unassigned name" :P
@Adám this seems to work
Feb 21, 2018 19:43
@EriktheOutgolfer You're right; I misspoke.
oh, :access public was required :p
@dzaima Oh, yes. Silly me. And now I remember that that works even though you really shouldn't be allowed to do that.
@Adám yeah... (although I don't think a destructor should contain ⎕OFF)
@dzaima I guess that does allow you to have a single clean-up function which you can call early, e.g. to reset the state of the instance when things went wrong or the user wants to start over.
@Adám Isthat a bug?
Feb 21, 2018 19:45
@wizzwizz4 A bug or a feature (^^), I'm not sure.
@EriktheOutgolfer Why not? E.g. an application could e.g. close APL (i.e. itself) when a certain object is closed by the user, rather than dumping the user to the session.
@Adám like PATCH.DWS does, and I find that a bit annoying :P
@Adám Is there really no way to override a parent class's behaviour?
@EriktheOutgolfer That's probably because APL must be restarted after patching. Windows sometimes forces a restart when updating critical parts of itself.
@Adám no, I don't think so, although sometimes it begs you for a restart constantly
@wizzwizz4 Sure there is. You can declare a method overridable with :Access Public Overridable
Feb 21, 2018 19:49
@Adám I mean with the constructors and destructors.
But that's good to know.
@wizzwizz4 Constructors doesn't make sense. How can the instance override the base constructor before it has been instantiated? If you don't want that constructor, you're using the wrong base class.
@EriktheOutgolfer What version are you on? It's forced since Windows 10 (and has done before that but then they turned it off).
@Adám Good point. I suppose the same logic holds for the destructor.
So can you call another base class's methods on your class?
@wizzwizz4 Also, there are two ways to override. If the derived class has an identically named method, it overrides the base class method for the derived class only, but if the identically named method is :Access Public Override, then even if code in the base class calls that method, it is redirected to the derived class one.
@wizzwizz4 "on your class"?
@Adám I meant "instance".
@wizzwizz4 You can call any public methods from anywhere. If they are in a base class, they of course need to be :Access public shared.
Feb 21, 2018 19:56
@Adám Can you call them as if the base class of your instance was the base class defining them?
Like Python's str.join (base.method) having a pseudoparameter at the beginning giving a reference to the object.
@wizzwizz4 You can put such common methods in a namespace, and then write :Include MyNamespace. This allows calling that namespace's methods as if they were in the base class. This way, multiple classes can share a set of methods, as if they had multiple inheritance.
Thank you for participating!
 
Conversation ended Feb 21, 2018 at 20:00.