{
"cells": [
{
"cell_type": "markdown",
"id": "64d97928",
"metadata": {},
"source": [
"# Inheritance and interfaces\n",
"\n",
"## Inheritance\n",
"\n",
"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.\n",
"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.\n",
"\n",
"Here's an example:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "d8a7ea2c",
"metadata": {},
"outputs": [],
"source": [
"]dinput\n",
":Class Person ⍝ Person base class\n",
" :field heightVal\n",
" :field weightVal\n",
" :field ageVal←0\n",
"\n",
" ∇ Birth(h w)\n",
" :Access public\n",
" :Implements constructor\n",
" (heightVal weightVal)←h w\n",
" ∇\n",
"\n",
" :Property height,weight,age\n",
" :Access public\n",
" ∇ r←Get x\n",
" r←⌊⍎x.Name,'Val'\n",
" ∇\n",
" :endproperty\n",
" \n",
" ∇ Grow cm\n",
" :Access public\n",
" heightVal+←cm\n",
" ∇\n",
"\n",
" ∇ Gain kg\n",
" :Access public\n",
" weightVal+←kg\n",
" ∇\n",
"\n",
" ∇ Lose kg\n",
" :Access public\n",
" weightVal-←kg\n",
" ∇\n",
"\n",
" ∇ Age y\n",
" :Access public\n",
" ageVal+←y\n",
" ∇\n",
"\n",
" :property BMI\n",
" :access public\n",
" ∇ bmi←Get\n",
" bmi←⌊0.5+weightVal÷×⍨heightVal÷100\n",
" ∇\n",
" :endproperty\n",
" \n",
":EndClass"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "bf1add78",
"metadata": {},
"outputs": [],
"source": [
"]dinput\n",
":Class American: Person\n",
" :field public ssn\n",
" ∇ Birth(w h)\n",
" :Access public\n",
" :Implements constructor :base w h\n",
" ssn←1↓∊('-'@1∘⍕¨⊢+¯1+?)1000 100 10000\n",
" ∇\n",
":EndClass"
]
},
{
"cell_type": "markdown",
"id": "030e9d45",
"metadata": {},
"source": [
"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. \n",
"\n",
"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.\n",
"\n",
"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.\n",
"\n",
"We can extend our classes further:"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "da4b7a8a",
"metadata": {},
"outputs": [],
"source": [
"]dinput\n",
":Class NorthAmerican : Person\n",
"\n",
" :field public language←'English'\n",
"\n",
" ∇ Birth args\n",
" :Access public\n",
" :Implements constructor :base args\n",
" ∇\n",
"\n",
":EndClass"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "a399d8c7",
"metadata": {},
"outputs": [],
"source": [
"]dinput\n",
":Class American : NorthAmerican\n",
"\n",
" :field public ssn\n",
"\n",
" ∇ Birth(w h)\n",
" :Access public\n",
" :Implements constructor :base w h\n",
" ssn←1↓∊('-'@1∘⍕¨⊢+¯1+?)1000 100 10000\n",
" ∇\n",
"\n",
":EndClass"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "03a20004",
"metadata": {},
"outputs": [],
"source": [
"]dinput\n",
":Class Canadian : NorthAmerican\n",
"\n",
" :field public sin\n",
"\n",
" ∇ Birth(w h)\n",
" :Access public\n",
" :Implements constructor :base w h\n",
" sin←1↓∊('-'@1∘⍕¨⊢+¯1+?)3⍴1000\n",
" ∇\n",
"\n",
":EndClass"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "3f62fab1",
"metadata": {},
"outputs": [],
"source": [
"]dinput\n",
":Class Swede : Person\n",
"\n",
" :field public pin\n",
" :field public language←'Swedish'\n",
"\n",
" ∇ Birth(w h)\n",
" :Access public\n",
" :Implements constructor :base w h\n",
" pin←(2↓⍕100⊥3↑⎕TS),'-'@1⍕10000+¯1+?10000\n",
" ∇\n",
"\n",
":EndClass"
]
},
{
"cell_type": "markdown",
"id": "75c0f5cc",
"metadata": {},
"source": [
"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.\n",
"\n",
"If you deal with a lot of such derivations, you may want to know the hierarchy of a certain class or instance.\n",
"Monadic [`⎕CLASS`](http://help.dyalog.com/latest/index.htm#Language/System%20Functions/class.htm) 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`](http://help.dyalog.com/latest/index.htm#Language/System%20Functions/instances.htm) gives you a vector of refs to all the instances of the given class. "
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "c15cdae4",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"┌────────────┬─────────────────┬──────────┐\n",
"│ #.Canadian │ #.NorthAmerican │ #.Person │\n",
"└────────────┴─────────────────┴──────────┘\n",
""
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/html": [
"┌─────────┬──────────┐\n",
"│ #.Swede │ #.Person │\n",
"└─────────┴──────────┘\n",
""
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/html": [
" Albert Bert Charlie Dave Erik\n",
""
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/html": [
" Albert Bert Charlie Dave\n",
""
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"c1 c2 c3←{⎕NEW Canadian ⍵}¨(3 50)(4 55)(6 60)\n",
"⎕CLASS c1\n",
"(c1 c2 c3).⎕DF 'Albert' 'Bert' 'Charlie'\n",
"a1←⎕NEW American (7.5 47)\n",
"a1.⎕DF 'Dave'\n",
"s1←⎕NEW Swede (5 70)\n",
"s1.⎕DF 'Erik'\n",
"⎕CLASS s1\n",
"⎕INSTANCES Person\n",
"⎕INSTANCES NorthAmerican"
]
},
{
"cell_type": "markdown",
"id": "313cf791",
"metadata": {},
"source": [
"There's another nice system function when dealing with classes (and other scripted objects); [`⎕SRC`](http://help.dyalog.com/latest/index.htm#Language/System%20Functions/src.htm) (SouRCe):"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "443a0dcc",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
":class cl \n",
"∇r←SetDF x \n",
":access public shared\n",
"⎕DF x \n",
"r←1 \n",
"∇ \n",
":endclass \n",
""
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"↑⎕SRC cl⊣(⎕FIX':class cl' '∇r←SetDF x' ':access public shared' '⎕DF x' 'r←1' '∇' ':endclass').SetDF'yo!'"
]
},
{
"cell_type": "markdown",
"id": "fbf51625",
"metadata": {},
"source": [
"## Interfaces\n",
"\n",
"A Dyalog [interface](http://help.dyalog.com/latest/index.htm#Language/Object%20Oriented%20Programming/Interfaces/Interfaces.htm) 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.\n",
"\n",
"Consider, for example, the following: "
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "2df62663",
"metadata": {},
"outputs": [],
"source": [
"]dinput\n",
":Interface FishBehaviour\n",
"∇ R←Swim ⍝ Returns description of swimming capability\n",
"∇\n",
":EndInterface ⍝ FishBehaviour"
]
},
{
"cell_type": "markdown",
"id": "3dfd882a",
"metadata": {},
"source": [
"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: "
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "fcf2d6b2",
"metadata": {},
"outputs": [],
"source": [
"]dinput\n",
":Interface BirdBehaviour\n",
"∇ R←Fly ⍝ Returns description of flying capability\n",
"∇\n",
"\n",
"∇ R←Lay ⍝ Returns description of egg-laying behaviour\n",
"∇\n",
"\n",
"∇ R←Sing ⍝ Returns description of bird-song\n",
"∇\n",
":EndInterface ⍝ BirdBehaviour"
]
},
{
"cell_type": "markdown",
"id": "bc36a9c0",
"metadata": {},
"source": [
"Now we can define a class with a base class, which implements these methods: "
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "3845f9fe",
"metadata": {},
"outputs": [],
"source": [
"]dinput\n",
":Class Penguin: Animal,BirdBehaviour,FishBehaviour\n",
" ∇ R←NoCanFly\n",
" :Implements Method BirdBehaviour.Fly\n",
" R←'Although I am a bird, I cannot fly'\n",
" ∇\n",
" ∇ R←LayOneEgg\n",
" :Implements Method BirdBehaviour.Lay\n",
" R←'I lay one egg every year'\n",
" ∇\n",
" ∇ R←Croak\n",
" :Implements Method BirdBehaviour.Sing\n",
" R←'Croak, Croak!'\n",
" ∇\n",
" ∇ R←Dive\n",
" :Implements Method FishBehaviour.Swim\n",
" R←'I can dive and swim like a fish'\n",
" ∇\n",
":EndClass ⍝ Penguin"
]
},
{
"cell_type": "markdown",
"id": "efe1a7fe",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "71240813",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Dyalog APL",
"language": "apl",
"name": "dyalog-kernel"
},
"language_info": {
"file_extension": ".apl",
"mimetype": "text/apl",
"name": "APL"
}
},
"nbformat": 4,
"nbformat_minor": 5
}