Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Modularity & Abstraction in Functional Programming

Chris
January 31, 2015

Modularity & Abstraction in Functional Programming

A talk on module systems given at Compose Conference 2015.
http://www.composeconference.org/

Chris

January 31, 2015
Tweet

More Decks by Chris

Other Decks in Programming

Transcript

  1. Modularity & Abstraction
    in
    Functional Programming
    Chris Martens
    Carnegie Mellon University
    Compose Conference 2015
    1

    View Slide

  2. What’s modularity for?
    How do we get it? (in SML)
    Type Theory
    Further Research
    2

    View Slide

  3. What’s modularity for?
    !
    !
    !
    3

    View Slide

  4. Abstraction
    4

    View Slide

  5. Composability
    5

    View Slide

  6. What’s modularity for?
    How do we get it?
    !
    !
    6

    View Slide

  7. — John Reynolds, 1983
    7

    View Slide

  8. Inductive Datatypes:
    Defined by how you build one
    datatype ‘a list = Nil | Cons of ‘a * ‘a list
    8

    View Slide

  9. Abstract Datatypes:
    Defined by how you use one
    9

    View Slide

  10. Abstract Datatypes:
    Defined by how you use one
    signature STACK =
    sig
    type 'a stack
    val empty : 'a stack
    val push : 'a -> 'a stack -> 'a stack
    val pop : 'a stack -> (‘a * 'a stack) option
    end
    10

    View Slide

  11. SML Signatures
    signature SIG_NAME =
    sig
    […declarations…]
    end
    11

    View Slide

  12. SML Signatures
    signature SIG_NAME =
    sig
    […declarations…]
    end
    Structures*
    structure StructName =
    struct
    [...definitions...]
    end
    *AKA modules
    12

    View Slide

  13. signature STACK =
    sig
    type 'a stack
    val empty : 'a stack
    val push : 'a -> 'a stack -> 'a stack
    val pop : 'a stack -> (‘a * 'a stack) option
    end
    structure ListStack : STACK =
    struct
    type 'a stack = 'a list
    val empty = []
    fun push x s = x::s
    fun pop (h::t) = SOME (h,t)
    | pop [] = NONE
    end
    Structures
    13

    View Slide

  14. signature STACK =
    sig
    type 'a stack
    val empty : 'a stack
    val push : 'a -> 'a stack -> 'a stack
    val pop : 'a stack -> (‘a * 'a stack) option
    end
    structure ListStack : STACK =
    struct
    type 'a stack = 'a list
    val empty = []
    fun push x s = x::s
    fun pop (h::t) = SOME (h,t)
    | pop [] = NONE
    end
    Structures
    14

    View Slide

  15. signature STACK =
    sig
    type 'a stack
    val empty : 'a stack
    val push : 'a -> 'a stack -> 'a stack
    val pop : 'a stack -> (‘a * 'a stack) option
    end
    structure ListStack : STACK =
    struct
    type 'a stack = 'a list
    val empty = []
    fun push x s = x::s
    fun pop (h::t) = SOME (h,t)
    | pop [] = NONE
    end
    Structures
    15

    View Slide

  16. signature STACK =
    sig
    type 'a stack
    val empty : 'a stack
    val push : 'a -> 'a stack -> 'a stack
    val pop : 'a stack -> (‘a * 'a stack) option
    end
    structure ListStack : STACK =
    struct
    type 'a stack = 'a list
    val empty = []
    fun push x s = x::s
    fun pop (h::t) = SOME (h,t)
    | pop [] = NONE
    end
    Structures
    16

    View Slide

  17. val s = ListStack.new;
    val s1 = ListStack.push “foo” s;
    val s2 = ListStack.push “bar” s1;
    val x = ListStack.pop s2;
    - x : string = “bar”
    Structures
    17

    View Slide

  18. Signature Matching
    If type t implemented as , then the values specified to have
    type t must have type [/t].*
    !
    *or a more general type.
    !
    sigs-to-structs are many-to-many
    18

    View Slide

  19. Many Signatures to 1 Structure
    ListStack : STACK
    !
    ListStack :
    sig
    val empty : 'a list
    val push : 'a -> 'a list -> 'a list
    end
    19

    View Slide

  20. Many Structures to 1 Signature
    Let’s extend our example…
    20

    View Slide

  21. signature STACK =
    sig
    type 'a stack
    val empty : 'a stack
    val push : 'a -> 'a stack -> 'a stack
    val pop : 'a stack -> ('a * 'a stack) option
    val size : 'a stack -> int
    end
    Extend the Signature
    21

    View Slide

  22. structure ListStack : STACK =
    struct
    type 'a stack = 'a list
    val new = []
    fun push x s = x::s
    fun pop (x::s) = SOME (x,s)
    | pop [] = NONE
    fun size s = List.length s
    end
    Extend the Structure
    22

    View Slide

  23. structure SizedListStack : STACK =
    struct
    type 'a stack = ('a list * int)
    val empty = ([], 0)
    fun push x (st, sz) = (x::st, sz + 1)
    fun pop (x::st, sz) = SOME (x, (st, sz - 1))
    | pop ([], _) = NONE
    fun size (st, sz) = sz
    end
    !
    A More Efficient Implementation
    23

    View Slide

  24. structure SizedListStack : STACK =
    struct
    type 'a stack = ('a list * int)
    val empty = ([], 0)
    fun push x (st, sz) = (x::st, sz + 1)
    fun pop (x::st, sz) = SOME (x, (st, sz - 1))
    | pop ([], _) = NONE
    fun size (st, sz) = sz
    end
    !
    A More Efficient Implementation
    24

    View Slide

  25. structure SizedListStack : STACK =
    struct
    type 'a stack = ('a list * int)
    val empty = ([], 0)
    fun push x (st, sz) = (x::st, sz + 1)
    fun pop (x::st, sz) = SOME (x, (st, sz - 1))
    | pop ([], _) = NONE
    fun size (st, sz) = sz
    end
    !
    A More Efficient Implementation
    25

    View Slide

  26. Client code:!
    !
    fn inspect
    (s : ‘a ListStack.stack) =
    (case s of [] => ...
    | (x::xs) => ...)
    Abstraction?
    26

    View Slide

  27. structure ListStack : STACK =
    struct
    datatype dt = Stack of 'a list
    type t = dt
    [...]
    end
    Abstraction through !
    Hidden Datatypes
    27

    View Slide

  28. structure ListStack :> STACK =
    struct
    type t = ‘a list
    [...]
    end
    Abstraction through !
    Opaque Ascription
    28

    View Slide

  29. From Abstraction to Composability
    SML: Functors!*
    *not to be confused with Haskell functors, Prolog functors,
    C++ functors, nor category-theoretic functors
    29

    View Slide

  30. Functors
    Parameterized structures
    expression : function :: module : functor
    30

    View Slide

  31. Functors
    functor MyFun (Arg : ARGSIG) : MY_SIG =
    struct
    (...defns possibly mentioning Arg.stuff...)
    end
    !
    structure MyArg : ARGSIG = ...
    !
    structure MyStruct = MyFun (MyArg)
    31

    View Slide

  32. Functor Example
    functor RPNCalculator (Stack : STACK) =
    struct
    datatype Oper = Num of int | Plus | Mul | …
    type rpn = Oper list
    fun compute (c : int Stack.stack) (eqn : rpn) =
    case eqn of [] => Stack.pop c
    | ((Num i)::eqn) => compute (Stack.push i) eqn
    | (Plus::eqn) => if (Stack.size c) < 2 then …
    else (* pop 2 off stack, add, push *) …
    end
    32

    View Slide

  33. Functor Application
    structure Calc1 = RPNCalculator (ListStack)
    structure Calc2 = RPNCalculator (SizedListStack)
    33

    View Slide

  34. Representation Independence
    Reynolds 1983:
    “Types, Abstraction, and Parametric Polymorphism.”
    Relational Parametricity, AKA “the abstraction theorem”
    Mitchell 1986:
    “Representation independence and data abstraction.”
    Application of relational parametricity.
    34

    View Slide

  35. Representation Independence
    “The behavior of clients of an ADT must be unaffected by
    changes to the internal representation of the ADT that are
    preserved by its operations.”
    https://wiki.mpi-sws.org/star/paramore
    35

    View Slide

  36. Representation Independence
    “The behavior of clients of an ADT must be unaffected by
    changes to the internal representation of the ADT that are
    preserved by its operations.”
    https://wiki.mpi-sws.org/star/paramore
    M ~ M’ => for all F, F(M) ~ F(M’)
    !
    36

    View Slide

  37. Representation Independence
    “The behavior of clients of an ADT must be unaffected by
    changes to the internal representation of the ADT that are
    preserved by its operations.”
    https://wiki.mpi-sws.org/star/paramore
    e.g.
    RPNCalculator(ListStack)
    and RPNCalculator(SizeListStack)
    should behave the same way.
    37

    View Slide

  38. Representation Independence
    What’s an example of two structures matching the
    same signature that don’t have a relation preserved
    by their operations?
    38

    View Slide

  39. Functors
    Example: tree traversal parameterized by a data
    structure to use as a worklist.
    39

    View Slide

  40. Functors Example: Tree Traversal
    signature WORKLIST =
    sig
    type 'a t
    val empty : 'a t
    val put : 'a t -> 'a -> 'a t
    val take : 'a t -> ('a * 'a t) option
    end
    40

    View Slide

  41. structure Stack : WORKLIST = …
    Functors Example: Tree Traversal
    41

    View Slide

  42. structure Queue : WORKLIST =
    struct
    type 'a t = 'a list
    val empty = []
    fun put q x = q @ [x]
    fun take (x::q) = SOME (x,q)
    | take [] = NONE
    end
    Functors Example: Tree Traversal
    42

    View Slide

  43. datatype 'a tree =
    Leaf | Node of 'a tree * 'a * 'a tree
    !
    functor Traverse (W : WORKLIST) =
    struct
    fun traverse (tr : 'a tree) : 'a list =
    [...]
    end
    Functors Example: Tree Traversal
    43

    View Slide

  44. fun traverse (tr : 'a tree) : 'a list =
    let fun traverse' (worklist : ('a tree) W.t) =
    (case S.take worklist of
    NONE => []
    | SOME (Leaf, rest) => traverse' rest
    | SOME (Node(t1,v,t2), rest) =>
    v :: (traverse' (W.put (W.put rest t1) t2)))
    in
    traverse' (W.put W.empty tr)
    end
    Functors Example: Tree Traversal
    44

    View Slide

  45. fun traverse (tr : 'a tree) : 'a list =
    let fun traverse' (worklist : ('a tree) W.t) =
    (case S.take worklist of
    NONE => []
    | SOME (Leaf, rest) => traverse' rest
    | SOME (Node(t1,v,t2), rest) =>
    v :: (traverse' (W.put (W.put rest t1) t2)))
    in
    traverse' (W.put W.empty tr)
    end
    Functors Example: Tree Traversal
    45

    View Slide

  46. fun traverse (tr : 'a tree) : 'a list =
    let fun traverse' (worklist : ('a tree) W.t) =
    (case S.take worklist of
    NONE => []
    | SOME (Leaf, rest) => traverse' rest
    | SOME (Node(t1,v,t2), rest) =>
    v :: (traverse' (W.put (W.put rest t1) t2)))
    in
    traverse' (W.put W.empty tr)
    end
    Functors Example: Tree Traversal
    46

    View Slide

  47. fun traverse (tr : 'a tree) : 'a list =
    let fun traverse' (worklist : ('a tree) W.t) =
    (case S.take worklist of
    NONE => []
    | SOME (Leaf, rest) => traverse' rest
    | SOME (Node(t1,v,t2), rest) =>
    v :: (traverse' (W.put (W.put rest t1) t2)))
    in
    traverse' (W.put W.empty tr)
    end
    Functors Example: Tree Traversal
    47

    View Slide

  48. structure DFS = Traverse (Stack)
    structure BFS = Traverse (Queue)
    Functors Example: Tree Traversal
    48

    View Slide

  49. Functors: Other Examples
    Typeclass-like uses: parameterize a module by values
    inhabiting “comparable” or “equality” types
    49

    View Slide

  50. Functors: Other Examples
    50
    signature ORD =
    sig
    type t
    val eq : t -> t -> bool
    val less : t -> t -> bool
    end

    View Slide

  51. Functors: Other Examples
    51
    signature ORD =
    sig
    type t
    val eq : t -> t -> bool
    val less : t -> t -> bool
    end
    signature SET =
    sig
    type elem
    type set
    val empty : set
    val add : elem -> set -> set
    […]
    end

    View Slide

  52. Functors: Other Examples
    52
    functor OrdSet (Elem : ORD) : SET =
    structure
    type elem = Elem.t
    type set = elem list
    […]
    end
    signature ORD =
    sig
    type t
    val eq : t -> t -> bool
    val less : t -> t -> bool
    end
    signature SET =
    sig
    type elem
    type set
    val empty : set
    val add : elem -> set -> set
    […]
    end

    View Slide

  53. Functors: Other Examples
    “A functor is a framework”
    !
    Functioning project: give me render, update, and event
    handler; I’ll put the pieces together into a game.
    53

    View Slide

  54. Functors: Other Examples
    fun loop s = [...]
    case option_iterate Game.tick s num_ticks of
    NONE => ()
    | SOME s =>
    (Game.render screen s;
    case SDL.pollevent () of
    NONE => (SDL.delay 0; loop s)
    | SOME e =>
    Option.app loop (Game.handle_event e s))
    54

    View Slide

  55. Recap…
    We’ve covered signatures, structures;
    how they relate (via matching);
    and functors, which allow for separate modular development
    via representation independence.
    !
    What kind of type theory underlies these principles?
    55

    View Slide

  56. What’s modularity for?
    How do we get it? (in SML)
    Type Theory
    !
    56

    View Slide

  57. Logical Content
    Quantifying over types (System F):
    !
    e : ∀ t:type. parametric polymorphism
    e : ∃ t:type. type abstraction
    57

    View Slide

  58. Logical Content
    “Abstract Types Have Existential Type,” Mitchell and Plotkin
    1988
    stack : ∀t. ∃ s. [s ⋀ (t ⋀ s -> s) ⋀ (s -> (t ⋀ s) ⋁ ⊤)]
    sig
    type ‘a stack
    emp : stack
    push : ‘a * ‘a stack -> ‘a stack
    pop : ‘a -> (‘a * ‘a stack) option
    end
    c.f.:
    58

    View Slide

  59. Logical Content
    “F-ing Modules,” Rossberg, Russo, and Dreyer 2010
    59
    A direct account of all the essential ML module system
    features in terms of System F.

    View Slide

  60. Q: Suppose you have a functor

    F(X:A) :> sig type t end
    and a module M:A.
    !
    Does
    F(M).t = F(M).t ?
    A stumbling block?
    60

    View Slide

  61. e.g.

    structure S1 = OrdSet(IntOrd) :> SET
    structure S2 = OrdSet(IntOrd) :> SET
    A stumbling block?
    61
    Does
    S1.add 6 (S2.empty)
    type check?

    View Slide

  62. A stumbling block?
    62
    In a straightforward account of abstract types as existential
    quantification: No.
    !
    In Standard ML: No. (Generative functors)
    In OCaml: Yes. (Applicative functors)
    Does
    S1.add 6 (S2.empty)
    type check?

    View Slide

  63. When generativity makes sense
    63
    functor F () : ORD = structure
    type t = int
    val eq = Int.eq
    val less =
    if random() then Int.less
    else Int.greater
    end
    !
    structure S1 = Set (F ())
    structure S2 = Set (F ())

    View Slide

  64. “Applicative/Generative = Pure/Impure, plain and simple.”!
    — Derek Dreyer, slides from WG2.8 Meeting, 2007
    Generative vs. Applicative Functors:
    64
    Unfortunately, accounting for the semantics of!
    applicative functors is hard!!
    !
    (see “F-ing Modules” expanded version)

    View Slide

  65. Functor Types in Various MLs
    65
    Generative
    Functors
    Applicative
    Functors
    Higher-order
    Functors
    Known!
    Unsoundness?
    Standard
    ML
    Yes No No No
    OCaml
    Yes
    (Recently)
    Yes No No
    Moscow
    ML
    Yes Yes Yes Yes

    View Slide

  66. Functor Types in Various MLs
    66
    Generative
    Functors
    Applicative
    Functors
    Higher-order
    Functors
    Known!
    Unsoundness?
    Standard
    ML
    Yes No No No
    OCaml
    Yes
    (Recently)
    Yes No No
    Moscow
    ML
    Yes Yes Yes Yes

    View Slide

  67. first-class modules!
    recursive modules!
    mix-in modules!
    !
    interpretation of objects & typeclasses!
    package management for Haskell (Backpack)
    Beyond ML Modules:
    67

    View Slide

  68. Modularity (= composability through abstraction)!
    is for reducing cognitive load & strict code dependencies when
    “programming in the large.”!
    !
    It can be enforced at the linguistic level through type structure !
    rather than managed by ad-hoc build system conventions.
    Takeaway
    68

    View Slide

  69. Give ML module systems a try!
    Takeaway (Alternate)
    69

    View Slide

  70. Reynolds - Types, Abstraction, & Parametric Polymorphism!
    Mitchell & Plotkin - Abstract Types Have Existential Type!
    Mitchell - Representation Independence and Data Abstraction!
    Derek Dreyer’s thesis & papers!
    Further Reading
    Code for this talk:
    https://github.com/chrisamaphone/compose2015
    @chrisamaphone
    70

    View Slide