A rambling tour of surprising things in several OO languages. These and other features were considered while I was designing Corinna, the next-generation OO syntax being proposed for the Perl core.
1. RUMMAGING IN THE CLOOSET
Curtis “Ovid” Poe
http://allaroundtheworld.fr/
9 juin 2021 Copyright 2020, http://www.allaroundtheworld.fr/
2. All Around The World
• Curtis “Ovid” Poe
• https://allaroundtheworld.fr/
• https://twitter.com/OvidPerl
• ovid@allroundtheworld.fr
↓
9 juin 2021 Copyright 2020, http://www.allaroundtheworld.fr/
3. OO Trivia Night
• No grand proposals
• No serious points
• Miscellaneous OO trivia
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
4. Simula 67
Class Rectangle (Width, Height); Real Width, Height;
! Class with two parameters;
Begin
Real Area, Perimeter; ! Attributes;
Procedure Update; ! Methods (Can be Virtual);
Begin
Area := Width * Height;
Perimeter := 2*(Width + Height)
End of Update;
Boolean Procedure IsSquare;
IsSquare := Width=Height;
Update; ! Life of rectangle started at creation;
OutText("Rectangle created: "); OutFix(Width,2,6);
OutFix(Height,2,6); OutImage
End of Rectangle;
! http://staff.um.edu.mt/jskl1/talk.html#Classes
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
7. Smalltalk
“Making simple things very simple and complex
things very possible”— Alan Kay
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
Via Dr. Stéphane Ducasse
http://scg.unibe.ch/archive/lectures/DucasseLectures/Duca00y1SmalltalkLectures.pdf
8. Syntax on a Postcard
exampleWithNumber: x
|y|
true & false not & (nil isNil) ifFalse: [self halt].
y := self size + super size.
#($a #a 'a' 1 1.0)
do: [:each | Transcript
show: (each class name);
show: (each printString);
show: ' '].
^ x < y
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
9. Smalltalk-80
• true
• false
• nil
• self
• super
• thisContext
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
10. Side Note: Lisp defined in Lisp
apply[fn;x;a] =
[atom[fn] → [eq[fn;CAR] → caar[x];
eq[fn;CDR] → cdar[x];
eq[fn;CONS] → cons[car[x];cadr[x]];
eq[fn;ATOM] → atom[car[x]];
eq[fn;EQ] → eq[car[x];cadr[x]];
T → apply[eval[fn;a];x;a]];
eq[car[fn];LAMBDA] → eval[caddr[fn]; pairlis[cadr[fn];x;a]];
eq[car[fn];LABEL] →
apply[caddr[fn];x;cons[cons[cadr[fn];caddr[fn]];a]]]
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
Lisp 1.5 Programmer's Manual, 1962. From page 13
http://www.softwarepreservation.org/projects/LISP/book/LISP%201.5%20Programmers%20Manual.pdf
11. No “if” in Smalltalk
result := a > b
ifTrue:[ 'greater' ]
ifFalse:[ 'less or equal' ]
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
12. Remember This Slide!
abstract class Boolean {
method ifTrue ($code) {}
method ifFalse ($code) {}
}
class True isa Boolean {
method ifTrue ($code) {$code->()}
}
class False isa Boolean {
method ifFalse ($code) {$code->()}
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
13. When I get bored writing slides …
class SuperPosition isa Boolean {
has $observed;
method ifTrue ($code) {
$observed //= 'true';
$code->() if 'true' eq $observed;
}
method ifFalse ($code) {
$observed //= 'false';
$code->() if 'false' eq $observed;
}
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
15. Inheritance
• class Customer isa Person {…}
• A more specialized version of the parent
• Liskov!
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
16. Barbara Liskov
Liskov Substitution Principle
Subtype Requirement: Let ϕ(x)
be a property provable about
objects x of type T.
Then ϕ(y) should be true for
objects y of type S where S is a
subtype of T.
Photo by Kenneth C. Zirkel
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
🤷🤷
17. Barbara Liskov
Liskov Substitution Principle
Any place you use a class, you
should be able to use its
subclasses.
Photo by Kenneth C. Zirkel
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
👍🤷
18. Liskov
# Assuming
class Customer isa Person { … }
# If this works
if ( $person->age('years') >= VOTING_AGE ) { … }
# The following must work
if ( $customer->age('years') >= VOTING_AGE ) { … }
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
20. Alan Kay
You're Doing It Wrong
• Invented the term “Object-
Oriented”
• Biological systems
• Inheritance was interesting
• Until the problem …
By Marcin Wichary from SF, U.S.A. -
Alan Kay, CC BY 2.0
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
22. Inheritance (sucks)
• Software doesn’t understand intent
• We can’t enforce “subclass as specialization”
• Design by Contract (sort of) helps
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
23. Design by Contract
• Formal declaration of a class "contract"
• Preconditions
• Postconditions
• Invariants
• Subclasses can weaken preconditions
• Subclasses can strengthen postconditions and
invariants
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
24. DbC in Eiffel
put (x: ELEMENT; key: STRING) is
-- Insert x so that it can be retrieved via key.
require
count <= capacity
not key.empty
do
... Some insertion algorithm ...
ensure
has (x)
item (key) = x
count = old count + 1
end
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
25. DbC In Perl (many CPAN modules)
package ClassName
use Class::Contract;
contract {
inherits 'BaseClass';
attr 'data1';
attr 'data2' => HASH;
class attr 'shared' => SCALAR;
ctor 'new';
method 'methodname';
pre { ... }; failmsg 'Error message';
post { ... }; failmsg 'Error message';
impl { ... };
method 'nextmethod';
impl { ... };
class method 'sharedmeth';
impl { ... };
};
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
26. BETA Programming Language
Employee: (#
name: @ Text;
birthday: @ Date;
dept: ^ Department;
totalHours: @ Integer;
RegisterWork: (#
noOfHours: @ Integer
enter noOfHours
do noOfHours + totalhours → totalHours
#);
ComputeSalary:< (#
salary: @ integer
do inner
exit salary
#);
#)
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
https://beta.cs.au.dk/Papers/BetaOverview/BetaOverview.pdf
29. Moose
package Employee {
use Moose;
sub ComputeSalary {
my $salary = inner();
croak(…) if $salary < 0;
return $salary;
}
}
package Salesman {
use Moose;
extends 'Employee';
augment ComputeSalary => sub {
my $self = shift;
…
return $salary;
};
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
30. Back to Inheritance
• Multiple inheritance
• Single single inheritance
• Composition and delegation
• Interfaces
• Mixins
• Whatever the BETA designers were smoking …
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
31. Mixins
module Bomb
def explode
puts "Bomb Explode"
end
def fuse
puts "Bomb Fuse"
end
end
module Toddler
def explode
puts "Toddler Explode"
end
def fuse
puts "Toddler Fuse"
end
end
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
35. Mixins
class Car < Vehicle
include Robot
…
end
• What happens if Vehicle and Robot both have a driver method?
• Robot wins (same is true of roles since we can't declare overrides)
• Is Robot really a more specialized version of Vehicle?
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
36. Mixins
• Should not make composition assumptions
• Thus, super should not be called (but it is)
• Should next::method be used in roles?
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
38. But Ovid …
• “It mostly works”
• “Easy to fix when it doesn’t”
• “Devs should know their code base”
(That last one really ticks me off)
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
39. Alan Kay
Human body has ~ 40 trillion cells
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
40. A Perfect Object
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
41. Web Browsers and Web Servers
• Browsers send messages
• Browser doesn't crash if server crashes
• Isolation, not encapsulation
• Well-defined, fault-tolerant protocol
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
42. Corinna v Perl v Python
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
49. InventoryItem (mutable!)
from dataclasses import dataclass
@dataclass
class InventoryItem:
name: str
unit_price: float
quantity_on_hand: int = 0
def assert_valid(self):
is_valid = True
for field_name, field_def in self.__dataclass_fields__.items():
actual_type = type(getattr(self, field_name))
if actual_type != field_def.type:
print(f"t{field_name}: '{actual_type}' instead of
'{field_def.type}'")
is_valid = False
if not is_valid:
raise ValueError("Type assertion failed");
def total_cost(self) -> float:
self.assert_valid()
return self.unit_price * self.quantity_on_hand
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
50. InventoryItem (immutable)
from dataclasses import dataclass
@dataclass(frozen=True)
class InventoryItem:
name: str
unit_price: float
quantity_on_hand: int = 0
def __postinit__(self):
self.assert_valid
def assert_valid(self):
is_valid = True
for field_name, field_def in self.__dataclass_fields__.items():
actual_type = type(getattr(self, field_name))
if actual_type != field_def.type:
print(f"t{field_name}: '{actual_type}' instead of '{field_def.type}'")
is_valid = False
if not is_valid:
raise ValueError("Type assertion failed");
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
51. Solving This
• Pydantic—Runtime type validation
• mypy—Static type checker
• Pyright—Static type checker
https://pydantic-docs.helpmanual.io/
http://mypy-lang.org/
https://github.com/microsoft/pyright
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
53. InventoryItem ☹️
class InventoryItem {
has $name :new :reader :isa(Str);
has $unit_price :new :reader :isa(Num);
has $quantity_on_hand :new :reader :isa(Int);
method total_cost() {
return $unit_price * $quantity_on_hand;
}
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
54. Why Not Types?
• Variable declarations
• Slot declarations
• Signatures
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
55. Why Not Types?
• my Int $foo;
• my $foo :Int;
• my $foo :isa(Int);
• has $foo :isa(Int);
• sub foo (Int $bar) {…}
• sub foo ($bar Int) {…}
• sub foo ($bar :isa(Int)) {…}
• sub foo ($bar :isa(Int)) :isa(Num) {…}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
56. Lessons Learned
• Inheritance is a mess
• We need (but won't get) optional types
• Simple is beautiful
• "Good enough" isn't good enough
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
57. Changes to Corinna
• No :builder
• No CONSTRUCT/BUILDARGS
• ADJUST and DESTRUCT are phasers
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
58. :builder
class SomeClass {
has $x :builder;
method _build_x () { return rand; }
}
class AnotherClass {
method _build_x () { return "Surprise!" }
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
59. Possible Fixes?
# painful if your builder is complex
class SomeClass {
has $x :builder { return rand };
}
# we don't yet have private methods
class SomeClass {
has $x :builder;
private method _build_x () { return "Surprise" }
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
60. For now …
class SomeClass {
has $x;
ADJUST {
$x = rand;
}
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
61. For now …
class SomeClass {
has $x;
ADJUST {
$x = $self->_build_x;
}
method _build_x () { return rand; }
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
62. Phasers
class SomeClass {
my $num_instances = 0;
ADJUST { $num_instances++ }
DESTRUCT { $num_instances-- }
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
63. Phasers?
class SomeClass {
my $num_instances = 0;
has $name :new;
CONSTRUCT ($class, @args) {
if (1 == @args) { unshift @args => 'name' }
return @args;
}
ADJUST { $num_instances++ }
DESTRUCT { $num_instances-- }
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
64. Alternate Constructors
class SomeClass {
my $num_instances = 0;
has $name :new;
method from_name ($this_name) {
return $class->new( name => $this_name );
}
ADJUST { $num_instances++ }
DESTRUCT { $num_instances-- }
}
my $thing = SomeClass->from_name('Ovid');
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
65. Speaking of Constructors …
class Person {
has $name :new;
has $title :new(optional);
has $answer = 42;
method name () {
return defined $title
? "$title $name" : $name;
}
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
66. Ugly!
class Person {
has $name :new;
has $title :new(optional);
has $answer = 42;
method name () {
return defined $title
? "$title $name" : $name;
}
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
67. Simplified by Reusing Syntax
class Person {
has $name :new;
has $title :new = undef;
has $answer = 42;
method name () {
return defined $title
? "$title $name" : $name;
}
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
68. Abstract Methods
abstract method foo ();
• Less useful without types
• Compile-time or runtime?
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
69. How can we simplify this?
abstract class Shape {
abstract method area ();
abstract method perimeter ();
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
70. Abstract Methods == Abstract Class
class Shape {
abstract method area ();
abstract method perimeter ();
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
71. Forward Declarations!
class Shape {
method area ();
method perimeter ();
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
72. Roles!
role Shape {
method area ();
method perimeter ();
}
# versus
role Shape {
requires qw(area perimeter);
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
73. The Future?
class Shape {
method area ();
method perimeter ();
}
# later
my $foo = Shape->any_method; # boom
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
74. Remember Me?
abstract class Boolean {
method ifTrue ($code) {}
method ifFalse ($code) {}
}
class True isa Boolean {
method ifTrue ($code) {$code->()}
}
class False isa Boolean {
method ifFalse ($code) {$code->()}
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
75. $self and $class
common method baz () {
$class->quux;
}
method foo () {
$self->bar;
$class->baz;
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
77. Redefined Warnings
method foo () {
my $class = ref ($self) || $self;
$self->quux;
}
"my" variable $class masks earlier declaration in
same scope at …
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
78. And in the future?
common method baz () {
$class->foo;
}
method foo () {
$self->bar;
$class->baz;
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
79. Quick Note On V1 Roles
• Might not have exclusion or aliasing
• Might not have method modifiers
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
80. Role Example
role Role::ToJSON {
use Cpanel::JSON::XS;
private method marshall_data ();
method to_json () {
my $data = $self->marshall_data;
return encode_json $data;
}
}
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
81. When?
• Curtis Poe—Ovid
• Corinna design
• Will create an RFC
• https://github.com/Perl/RFCs
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
82. When?
• Paul Evans—LeoNerd
• Object::Pad
• Future::AsyncAwait
• Syntax::Keyword::Try
• Much more
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
83. Summary
• This was trivia, not really informative
• What can we remove?
• What must we add?
• Lack of types is a problem
9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
84. 9 juin 2021 Copyright 2021, http://www.allaroundtheworld.fr/
Thank You!
Editor's Notes
We’re called in when you need to know it’s going to work.
For almost everyone, there will be some surprised here
These are the six reserved keywords. Wait, where's if/else?
Corinna syntax, duh … (singletons!)
Languages from specs are harder to make portable. Languages defined in terms of themselves are easy to make portable.
Corinna syntax, duh …
Even AGI (artificial general intelligence) would struggle with this when humans get this wrong all the time