Functional OOP, Clojure Style Yoav Rubin
About me • Software engineer at IBM Research - Haifa – Development of development environments – Large scale products to small scale research projects • Lecture the course “Functional programming on the JVM” in Haifa University {:name Yoav Rubin, :email yoavrubin@gmail.com, :blog http://yoavrubin.blogspot.com, :twitter @yoavrubin}
First thing first
Alan Kay
Alan Kay Edsger W. Dijkstra
Alan Kay Edsger W. Dijkstra
“Perspective is worth 80 IQ points” Alan Kay
• OOP • Clojure Agenda in
What’s in a software • Data types that describe the elements of the domain (nouns) • State changing operations (verbs)
Type functionality f1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X The software matrix
Type functionality f1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X The software matrix Data types, nouns
Type functionality f1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X The software matrix API, verbs, interfaces
Type functionality f1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X The software matrix implementations of operationx by Typey
Data directed programming The expression problem Philip Wadler SICP Deciding which function to use based on given data How to add rows and columns to the matrix without recompiling while preserving static typing
Let’s talk OOP • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z
Let’s talk OOP • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z Abstraction
Let’s talk OOP • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z
Let’s talk OOP • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z Information hiding
Let’s talk OOP • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z
Let’s talk OOP • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z Polymorphism
Let’s talk OOP • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z
Let’s talk OOP • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z Inheritance
Type functionality f1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X Inheritance
Type functionality f1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X Inheritance T4 can say that it is a T3
Type functionality f1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X Inheritance T4 can say that it is a T3 and its implementation of f3 is found at T3 X
OOP on the matrix • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in Class Z means that X is-a Z
How is it all related to Clojure?
What is Clojure
What is Clojure • A Lisp • A functional language • Dynamically typed • Emphasis on immutability • Treats concurrency as an elementary part of life – Not as a burden • Compiles to bytecode – Of the JVM / CLR / JS (as the web’s bytecode) • Excellent “great ideas to WTF” ratio
General structure • A Clojure project is built of namespaces • In each namespace there are functions and data elements • Functions can be either public or private – Either visible or not visible outside of the namespace
General structure • A Clojure project is built of namespaces • In each namespace there are functions and data elements • Functions can be either public or private – Either visible or not visible outside of the namespace Functional Information hiding
How to define rows in Clojure?
Creating new types • Metaobjects – a mechanism that allows description and creation of new datatypes • We can create our own metaobjects – E.g., a map that one of its key is “type” • In Clojure there are two metaobjects – Type – Record
The Type metaobject • Upon definition we need to provide: – Name – Member fields – APIs to implement and their implementation • Override methods from Object, interfaces, protocols (soon) • Cannot introduce new APIs to the matrix • Can be made mutable
The type metaobject Definition: Instantiation: Usage:
The type metaobject Definition: Instantiation: Usage:
The type metaobject Definition: Instantiation: Usage:
The type metaobject Definition: Instantiation: Usage:
Two main use cases • You really know what you are doing • You’re doing it wrong
The Record metaobject • Similar to the Type metaobject • Provides a map like behavior • No mutability
So far in the software matrix • Added new rows – New types / records – No new APIs • Associate with an existing column
So far in the software matrix • Added new rows – New types / records – No new APIs • Associate with an existing column Functional Abstraction
So far in the software matrix • Added new rows – New types / records – No new APIs • Associate with an existing column Functional Abstraction Polymorphism
How to define columns in Clojure?
Adding new APIs • New APIs for one type – just add a new function • Problem: how to handle more types? • Naïve solution: a simple dispatcher
Now there’s a new tree in town
What can a developer do? • Re-write the existing tree-map function – Because editing legacy code is fun… • Create another tree-map in another namespace and qualify its calls – Name collisions • Create tree-map2 – Complicating both developer’s and user’s code
A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X
A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API)
A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API) Two implementations
A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API) Two implementations QuadTree
A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API) Two implementations QuadTree ?
A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API) Two implementations QuadTree ? Tree-map did too much!!!
In the software matrix: Need to decomplect the creation of columns headers from cell marking Or in software design language: We need to separate the definition of an API from its implementation
Creating abstract APIs • No concrete implementation • Define a semantic unit – A set of behaviors that compose an API • In another place define the mapping between data types and the API – Marking of a cell in the matrix
Protocol • A set of several function signatures – Just the signature, without implementation – Dispatch is done based on the run-time type
Protocol • A set of several function signatures – Just the signature, without implementation – Dispatch is done based on the run-time type The protocol name
Protocol • A set of several function signatures – Just the signature, without implementation – Dispatch is done based on the run-time type The protocol name A function signature (there can be several of these)
Protocols and types • The linking of a protocol to a type can be done not as part of the definition of the type • This results in the possibility to extend existing, compiled types – Extend String – Extend even nil
Added to an existing type a new API Without changing the type
Added to an existing type a new API Without changing the type Functional polymorphism
Still, there are limitations Protocols allow type based dispatch only
Multi methods • Polymorphism which is based on a user defined dispatching function • The result of the execution of the dispatch function determines which implementation will be executes
(dispatch-fn) take-care-of ::moon ::sun (tco-sun) (tco-moon) dispatcher
(dispatch-fn) take-care-of ::moon ::sun (tco-sun) (tco-moon) This is the exposed API dispatcher
(dispatch-fn) take-care-of ::moon ::sun (tco-sun) (tco-moon) dispatcher
(dispatch-fn) take-care-of ::moon ::sun (tco-sun) (tco-moon) dispatcher
(dispatch-fn) take-care-of ::moon ::sun (tco-sun) ::lightning (tco-lightning) (tco-moon) dispatcher
The multi method name
The dispatching function
Meanwhile, at other namespaces
Meanwhile, at other namespaces
Meanwhile, at other namespaces
Multi method • We can use the same API for different data elements • All we need to know is that they obey that API • We can introduce new APIs for existing types
Multi method • We can use the same API for different data elements • All we need to know is that they obey that API • We can introduce new APIs for existing types Functional polymorphism
Is-a relationship • We can define that A is-a B • The dispatcher would handle A the same way that it handles B • (derive ::A ::B) – if the dispatch function return ::A – if no value is found for ::A in the dispatcher – Handle it as ::B
Is-a relationship
Is-a relationship
Is-a relationship
Is-a relationship
Is-a relationship
Is-a relationship
Why did it work • Derive harms the referential transparency of the multi method – The return value may differ if (derive…) was called – Referential transparency is our friend • Derive works only with namespace bound keywords – Those that start with :: • Clojure localizes the effect of mutability to the namespace
Type functionality f1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X T4 isa T2 (for f2) T4 isa T3 (for f3)
Type functionality f1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X T4 isa T2 (for f2) T4 isa T3 (for f3) Functional inheritance
Summary • We’ve seen – Functional abstraction – Functional information hiding – Functional polymorphism – Functional inheritance
Summary • We’ve seen functional – Abstraction – Information hiding – Polymorphism – Inheritance
Summary • We’ve seen functional OOP
Summary • We’ve seen functional OOP Clojure Style
Thank You!

Functional OOP, Clojure style

  • 2.
  • 3.
    About me • Softwareengineer at IBM Research - Haifa – Development of development environments – Large scale products to small scale research projects • Lecture the course “Functional programming on the JVM” in Haifa University {:name Yoav Rubin, :email yoavrubin@gmail.com, :blog http://yoavrubin.blogspot.com, :twitter @yoavrubin}
  • 4.
  • 5.
  • 6.
    Alan Kay EdsgerW. Dijkstra
  • 7.
    Alan Kay EdsgerW. Dijkstra
  • 8.
    “Perspective is worth80 IQ points” Alan Kay
  • 9.
  • 10.
    What’s in asoftware • Data types that describe the elements of the domain (nouns) • State changing operations (verbs)
  • 11.
    Type functionalityf1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X The software matrix
  • 12.
    Type functionalityf1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X The software matrix Data types, nouns
  • 13.
    Type functionalityf1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X The software matrix API, verbs, interfaces
  • 14.
    Type functionalityf1 f2 f3 f4 T1 X X T2 X T3 X T4 X X X X The software matrix implementations of operationx by Typey
  • 15.
    Data directed programming Theexpression problem Philip Wadler SICP Deciding which function to use based on given data How to add rows and columns to the matrix without recompiling while preserving static typing
  • 16.
    Let’s talk OOP •The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z
  • 17.
    Let’s talk OOP •The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z Abstraction
  • 18.
    Let’s talk OOP •The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z
  • 19.
    Let’s talk OOP •The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z Information hiding
  • 20.
    Let’s talk OOP •The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z
  • 21.
    Let’s talk OOP •The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z Polymorphism
  • 22.
    Let’s talk OOP •The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z
  • 23.
    Let’s talk OOP •The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in class Z means that X is-a Z Inheritance
  • 24.
    Type functionality f1 f2f3 f4 T1 X X T2 X T3 X T4 X X X Inheritance
  • 25.
    Type functionality f1 f2f3 f4 T1 X X T2 X T3 X T4 X X X Inheritance T4 can say that it is a T3
  • 26.
    Type functionality f1 f2f3 f4 T1 X X T2 X T3 X T4 X X X Inheritance T4 can say that it is a T3 and its implementation of f3 is found at T3 X
  • 27.
    OOP on thematrix • The rows are the classes – The domain abstractions we define and use – Which can hide within them their internal state • The column headers are interfaces – Which allow polymorphic usage • Marked cell in row X and column Y signifies that class X implements interface Y – Saying that the implementation itself resides in Class Z means that X is-a Z
  • 28.
    How is itall related to Clojure?
  • 29.
  • 30.
    What is Clojure •A Lisp • A functional language • Dynamically typed • Emphasis on immutability • Treats concurrency as an elementary part of life – Not as a burden • Compiles to bytecode – Of the JVM / CLR / JS (as the web’s bytecode) • Excellent “great ideas to WTF” ratio
  • 31.
    General structure • AClojure project is built of namespaces • In each namespace there are functions and data elements • Functions can be either public or private – Either visible or not visible outside of the namespace
  • 32.
    General structure • AClojure project is built of namespaces • In each namespace there are functions and data elements • Functions can be either public or private – Either visible or not visible outside of the namespace Functional Information hiding
  • 33.
    How to definerows in Clojure?
  • 34.
    Creating new types •Metaobjects – a mechanism that allows description and creation of new datatypes • We can create our own metaobjects – E.g., a map that one of its key is “type” • In Clojure there are two metaobjects – Type – Record
  • 35.
    The Type metaobject •Upon definition we need to provide: – Name – Member fields – APIs to implement and their implementation • Override methods from Object, interfaces, protocols (soon) • Cannot introduce new APIs to the matrix • Can be made mutable
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
    Two main usecases • You really know what you are doing • You’re doing it wrong
  • 41.
    The Record metaobject •Similar to the Type metaobject • Provides a map like behavior • No mutability
  • 42.
    So far inthe software matrix • Added new rows – New types / records – No new APIs • Associate with an existing column
  • 43.
    So far inthe software matrix • Added new rows – New types / records – No new APIs • Associate with an existing column Functional Abstraction
  • 44.
    So far inthe software matrix • Added new rows – New types / records – No new APIs • Associate with an existing column Functional Abstraction Polymorphism
  • 45.
    How to definecolumns in Clojure?
  • 46.
    Adding new APIs •New APIs for one type – just add a new function • Problem: how to handle more types? • Naïve solution: a simple dispatcher
  • 49.
    Now there’s anew tree in town
  • 50.
    What can adeveloper do? • Re-write the existing tree-map function – Because editing legacy code is fun… • Create another tree-map in another namespace and qualify its calls – Name collisions • Create tree-map2 – Complicating both developer’s and user’s code
  • 51.
    A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X
  • 52.
    A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API)
  • 53.
    A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API) Two implementations
  • 54.
    A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API) Two implementations QuadTree
  • 55.
    A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API) Two implementations QuadTree ?
  • 56.
    A deeper look Type functionality f1 f2 f3 tree-map GeneralTree X X X YetAnotherType X BinaryTree X X New column header (API) Two implementations QuadTree ? Tree-map did too much!!!
  • 57.
    In the softwarematrix: Need to decomplect the creation of columns headers from cell marking Or in software design language: We need to separate the definition of an API from its implementation
  • 58.
    Creating abstract APIs •No concrete implementation • Define a semantic unit – A set of behaviors that compose an API • In another place define the mapping between data types and the API – Marking of a cell in the matrix
  • 59.
    Protocol • A setof several function signatures – Just the signature, without implementation – Dispatch is done based on the run-time type
  • 60.
    Protocol • A setof several function signatures – Just the signature, without implementation – Dispatch is done based on the run-time type The protocol name
  • 61.
    Protocol • A setof several function signatures – Just the signature, without implementation – Dispatch is done based on the run-time type The protocol name A function signature (there can be several of these)
  • 62.
    Protocols and types •The linking of a protocol to a type can be done not as part of the definition of the type • This results in the possibility to extend existing, compiled types – Extend String – Extend even nil
  • 67.
    Added to anexisting type a new API Without changing the type
  • 68.
    Added to anexisting type a new API Without changing the type Functional polymorphism
  • 69.
    Still, there arelimitations Protocols allow type based dispatch only
  • 70.
    Multi methods • Polymorphismwhich is based on a user defined dispatching function • The result of the execution of the dispatch function determines which implementation will be executes
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 77.
  • 78.
  • 79.
  • 80.
  • 83.
  • 84.
    Multi method • Wecan use the same API for different data elements • All we need to know is that they obey that API • We can introduce new APIs for existing types
  • 85.
    Multi method • Wecan use the same API for different data elements • All we need to know is that they obey that API • We can introduce new APIs for existing types Functional polymorphism
  • 86.
    Is-a relationship • Wecan define that A is-a B • The dispatcher would handle A the same way that it handles B • (derive ::A ::B) – if the dispatch function return ::A – if no value is found for ::A in the dispatcher – Handle it as ::B
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
    Why did itwork • Derive harms the referential transparency of the multi method – The return value may differ if (derive…) was called – Referential transparency is our friend • Derive works only with namespace bound keywords – Those that start with :: • Clojure localizes the effect of mutability to the namespace
  • 94.
    Type functionality f1 f2f3 f4 T1 X X T2 X T3 X T4 X X X X T4 isa T2 (for f2) T4 isa T3 (for f3)
  • 95.
    Type functionality f1 f2f3 f4 T1 X X T2 X T3 X T4 X X X X T4 isa T2 (for f2) T4 isa T3 (for f3) Functional inheritance
  • 96.
    Summary • We’ve seen –Functional abstraction – Functional information hiding – Functional polymorphism – Functional inheritance
  • 97.
    Summary • We’ve seenfunctional – Abstraction – Information hiding – Polymorphism – Inheritance
  • 98.
  • 99.
    Summary • We’ve seenfunctional OOP Clojure Style
  • 100.