Modularity à la ML

Modularity à la ML

7cefc64f7b1b53513625bf3487ecd16d?s=128

Ionuț G. Stan

April 07, 2017
Tweet

Transcript

  1. 2.

    • Software Developer at • Worked with Scala for the

    past 5 years • FP, programming languages, compilers • Mostly-tech blog at igstan.ro About Me
  2. 3.
  3. 4.
  4. 5.
  5. 7.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map option f = case option of NONE => NONE | SOME a => SOME (f a) end
  6. 8.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map option f = case option of NONE => NONE | SOME a => SOME (f a) end
  7. 9.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map option f = case option of NONE => NONE | SOME a => SOME (f a) end
  8. 10.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map option f = case option of NONE => NONE | SOME a => SOME (f a) end
  9. 11.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map option f = case option of NONE => NONE | SOME a => SOME (f a) end
  10. 12.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f f = case option of NONE => NONE | SOME a => SOME (f a) end
  11. 13.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f option = case option of NONE => NONE | SOME a => SOME (f a) end
  12. 14.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f option = case option of NONE => NONE | SOME a => SOME (f a) end
  13. 15.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f option = case option of NONE => NONE | SOME a => SOME (f a) end
  14. 16.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f option = case option of NONE => NONE | SOME a => SOME (f a) end
  15. 17.

    $ sml - datatype 'a option = … NONE …

    | SOME of 'a; datatype 'a option = NONE | SOME of 'a - - fun map f option = … case option of … NONE => NONE … | SOME a => SOME (f a); val map = fn : ('a -> 'b) -> 'a option -> 'b option - - val a = SOME 1; val a = SOME 1 : int option - - map (fn a => a + 1) a; val it = SOME 2 : int option
  16. 18.

    $ sml - datatype 'a option = … NONE …

    | SOME of 'a; datatype 'a option = NONE | SOME of 'a - - fun map f option = … case option of … NONE => NONE … | SOME a => SOME (f a); val map = fn : ('a -> 'b) -> 'a option -> 'b option - - val a = SOME 1; val a = SOME 1 : int option - - map (fn a => a + 1) a; val it = SOME 2 : int option
  17. 19.

    $ sml - datatype 'a option = … NONE …

    | SOME of 'a; datatype 'a option = NONE | SOME of 'a - - fun map f option = … case option of … NONE => NONE … | SOME a => SOME (f a); val map = fn : ('a -> 'b) -> 'a option -> 'b option - - val a = SOME 1; val a = SOME 1 : int option - - map (fn a => a + 1) a; val it = SOME 2 : int option
  18. 20.

    $ sml - datatype 'a option = … NONE …

    | SOME of 'a; datatype 'a option = NONE | SOME of 'a - - fun map f option = … case option of … NONE => NONE … | SOME a => SOME (f a); val map = fn : ('a -> 'b) -> 'a option -> 'b option - - val a = SOME 1; val a = SOME 1 : int option - - map (fn a => a + 1) a; val it = SOME 2 : int option
  19. 21.

    $ sml - datatype 'a option = … NONE …

    | SOME of 'a; datatype 'a option = NONE | SOME of 'a - - fun map f option = … case option of … NONE => NONE … | SOME a => SOME (f a); val map = fn : ('a -> 'b) -> 'a option -> 'b option - - val a = SOME 1; val a = SOME 1 : int option - - map (fn a => a + 1) a; val it = SOME 2 : int option
  20. 22.

    $ sml - datatype 'a option = … NONE …

    | SOME of 'a; datatype 'a option = NONE | SOME of 'a - - fun map f option = … case option of … NONE => NONE … | SOME a => SOME (f a); val map = fn : ('a -> 'b) -> 'a option -> 'b option - - val a = SOME 1; val a = SOME 1 : int option - - map (fn a => a + 1) a; val it = SOME 2 : int option
  21. 23.

    $ sml - datatype 'a option = … NONE …

    | SOME of 'a; datatype 'a option = NONE | SOME of 'a - - fun map f option = … case option of … NONE => NONE … | SOME a => SOME (f a); val map = fn : ('a -> 'b) -> 'a option -> 'b option - - val a = SOME 1; val a = SOME 1 : int option - - map (fn a => a + 1) a; val it = SOME 2 : int option
  22. 24.

    $ sml - datatype 'a option = … NONE …

    | SOME of 'a; datatype 'a option = NONE | SOME of 'a - - fun map f option = … case option of … NONE => NONE … | SOME a => SOME (f a); val map = fn : ('a -> 'b) -> 'a option -> 'b option - - val a = SOME 1; val a = SOME 1 : int option - - map (fn a => a + 1) a; val it = SOME 2 : int option
  23. 25.

    $ sml - datatype 'a option = … NONE …

    | SOME of 'a; datatype 'a option = NONE | SOME of 'a - - fun map f option = … case option of … NONE => NONE … | SOME a => SOME (f a); val map = fn : ('a -> 'b) -> 'a option -> 'b option - - val a = SOME 1; val a = SOME 1 : int option - - map (fn a => a + 1) a; val it = SOME 2 : int option
  24. 26.
  25. 27.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f option = case option of NONE => NONE | SOME a => SOME (f a) end
  26. 28.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f option = case option of NONE => NONE | SOME a => SOME (f a) end
  27. 29.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f option = case option of NONE => NONE | SOME a => SOME (f a) end
  28. 30.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f option = case option of NONE => NONE | SOME a => SOME (f a) end
  29. 31.

    structure Option = struct datatype 'a option = NONE |

    SOME of 'a fun map f option = case option of NONE => NONE | SOME a => SOME (f a) end
  30. 32.

    $ sml - use "option.sml"; - - val a =

    Option.SOME 1; val a = SOME 1 : int Option.option - - Option.map (fn a => a + 1) a; val it = SOME 2 : int Option.option
  31. 33.

    $ sml - use "option.sml"; - - val a =

    Option.SOME 1; val a = SOME 1 : int Option.option - - Option.map (fn a => a + 1) a; val it = SOME 2 : int Option.option
  32. 34.

    $ sml - use "option.sml"; - - val a =

    Option.SOME 1; val a = SOME 1 : int Option.option - - Option.map (fn a => a + 1) a; val it = SOME 2 : int Option.option
  33. 35.

    $ sml - use "option.sml"; - - val a =

    Option.SOME 1; val a = SOME 1 : int Option.option - - Option.map (fn a => a + 1) a; val it = SOME 2 : int Option.option
  34. 36.

    $ sml - use "option.sml"; - - val a =

    Option.SOME 1; val a = SOME 1 : int Option.option - - Option.map (fn a => a + 1) a; val it = SOME 2 : int Option.option
  35. 37.
  36. 38.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  37. 39.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  38. 40.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  39. 41.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  40. 42.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  41. 43.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  42. 44.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  43. 46.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  44. 47.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  45. 48.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  46. 49.

    structure IntListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case Int.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  47. 50.

    structure StringListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case String.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  48. 51.

    structure StringListSet = struct val empty = [] fun add

    set elem = case set of [] => [elem] | head :: tail => case String.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  49. 52.

    functor ListSet(Elem : ORD) = struct val empty = []

    fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  50. 53.

    functor ListSet(Elem : ORD) = struct val empty = []

    fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  51. 54.

    In Standard ML, a functor is a module-level function that

    takes a module as argument and produces a module as a result.
  52. 55.

    Note: There's no relationship between an SML functor and the

    Functor type-class as defined by the cats or scalaz libraries.
  53. 56.

    functor ListSet(Elem : ORD) = struct val empty = []

    fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  54. 57.

    functor ListSet(Elem : ORD) = struct val empty = []

    fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  55. 59.

    functor ListSet(Elem : ORD) = struct val empty = []

    fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  56. 60.

    functor ListSet(Elem : ORD) = struct val empty = []

    fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  57. 61.

    functor ListSet(Elem : ORD) = struct val empty = []

    fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  58. 62.

    functor ListSet(Elem : ORD) = struct val empty = []

    fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  59. 67.

    A signature can be seen as the type of a

    module. It specifies the types and values that a module must define.
  60. 68.

    functor ListSet(Elem : ORD) = struct val empty = []

    fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  61. 69.

    functor ListSet(Elem : ORD) : SET = struct val empty

    = [] fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  62. 70.

    functor ListSet(Elem : ORD) : SET = struct val empty

    = [] fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  63. 71.

    signature SET = sig structure Key : ORD type t

    val empty : t val add : t -> key -> t end
  64. 72.

    signature SET = sig type t type key val empty

    : t val add : t -> key -> t end
  65. 73.

    signature SET = sig type t type key val empty

    : t val add : t -> key -> t end
  66. 74.

    signature SET = sig type t type key val empty

    : t val add : t -> key -> t end
  67. 75.

    signature SET = sig type t type key val empty

    : t val add : t -> key -> t end
  68. 76.

    signature SET = sig type t type key val empty

    : t val add : t -> key -> t end
  69. 77.

    signature SET = sig type t type key val empty

    : t val add : t -> key -> t end
  70. 78.

    functor ListSet(Elem : ORD) : SET = struct type t

    = Elem.t list type key = Elem.t val empty = [] fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  71. 79.

    functor ListSet(Elem : ORD) : SET = struct type t

    = Elem.t list type key = Elem.t val empty = [] fun add set elem = case set of [] => [elem] | head :: tail => case Elem.compare (head, elem) of LESS => head :: (add tail elem) | EQUAL => set | GREATER => elem :: set end
  72. 80.

    structure IntOrd = struct type t = Int.int val compare

    = Int.compare end structure IntListSet = ListSet(IntOrd) structure StringListSet = ListSet(struct type t = String.string val compare = String.compare end)
  73. 81.

    structure IntOrd = struct type t = Int.int val compare

    = Int.compare end structure IntListSet = ListSet(IntOrd) structure StringListSet = ListSet(struct type t = String.string val compare = String.compare end)
  74. 82.

    structure IntOrd = struct type t = Int.int val compare

    = Int.compare end structure IntListSet = ListSet(IntOrd) structure StringListSet = ListSet(struct type t = String.string val compare = String.compare end)
  75. 90.

    Limitations in SML Q If a functor is like a

    function, can we pass functors to functors, just like we can pass functions to functions?
  76. 91.

    Limitations in SML Q If a functor is like a

    function, can we pass functors to functors, just like we can pass functions to functions? A No. Standard ML does not have higher-order functors. OCaml and some other ML dialects have it, though.
  77. 94.

    Limitations in SML Q So we can't return functors from

    functors, either? A No, we cannot in Standard ML.
  78. 96.

    Limitations in Scala Q If Scala classes are the equivalent

    of SML functors, are they higher-order or not?
  79. 97.

    Limitations in Scala Q If Scala classes are the equivalent

    of SML functors, are they higher-order or not? A They're not. One cannot, save for reflection, pass classes as arguments to classes or produce classes from classes.
  80. 99.

    Limitations in SML Q In Scala, we can store objects

    in variables, pass them to functions or return them from functions. Does SML allow this with structures and functors?
  81. 100.

    Limitations in SML Q In Scala, we can store objects

    in variables, pass them to functions or return them from functions. Does SML allow this with structures and functors? A No. In Standard ML, modules are not first-class. Values and modules form two different, separate languages — the so- called core and module languages.
  82. 105.

    object IntOrd extends Ord { type T = Int def

    compare(a: T, b: T): Int = a - b }
  83. 106.

    trait Set { type T type K def empty: T

    def add(set: T, key: K): T }
  84. 107.

    class ListSet(val ord: Ord) extends Set { type K =

    ord.T type T = List[ord.T] def empty: T = List.empty def add(set: T, key: K): T = ??? }
  85. 108.

    object TwitterClient { object UserSet extends ListSet(UserOrd) def followers(username: String)

    = { val users: UserSet.T = UserSet.empty // add users and return them users } }
  86. 109.

    object TwitterClient { object UserSet extends ListSet(UserOrd) def followers(username: String)

    = { val users: UserSet.T = UserSet.empty // add users and return them users } }
  87. 110.

    object TwitterClient { object UserSet extends ListSet(UserOrd) def followers(username: String)

    = { val users: UserSet.T = UserSet.empty // add users and return them users } }
  88. 111.

    object TwitterClient { object UserSet extends ListSet(UserOrd) def followers(username: String)

    = { val users = UserSet.empty // add users and return them users } }
  89. 112.

    object TwitterClient { object UserSet extends ListSet(UserOrd) def followers(username: String)

    = { val users = UserSet.empty // add users and return them users } }
  90. 113.

    object TwitterClient { def followers(username: String) = { object UserSet

    extends ListSet(UserOrd) val users = UserSet.empty // add users and return them users } }
  91. 114.

    object TwitterClient { def followers(username: String) = { val userSet

    = new ListSet(UserOrd) val users = UserSet.empty // add users and return them users } }
  92. 115.

    object TwitterClient { def followers(username: String) = { val userSet

    = new ListSet(UserOrd) val users: UserSet.T = UserSet.empty // add users and return them users } }
  93. 116.

    object TwitterClient { def followers(username: String) = { val userSet

    = new ListSet(UserOrd) val users: userSet.T = UserSet.empty // add users and return them users } }
  94. 117.

    object TwitterClient { def followers(username: String): List[userSet.ord.T] forSome { val

    userSet: ListSet } = { val userSet = new ListSet(UserOrd) val users: userSet.T = UserSet.empty // add users and return them users } }
  95. 118.

    object TwitterClient { def followers(username: String): List[userSet.ord.T] forSome { val

    userSet: ListSet } = { val userSet = new ListSet(UserOrd) val users: userSet.T = UserSet.empty // add users and return them users } }
  96. 119.

    object TwitterClient { import scala.language.existentials def followers(username: String): List[userSet.ord.T] forSome

    { val userSet: ListSet } = { val userSet = new ListSet(UserOrd) val users: userSet.T = UserSet.empty // add users and return them users } }
  97. 120.

    object TwitterClient { import scala.language.existentials def followers(username: String): userSet.T forSome

    { val userSet: Set } = { val userSet = new ListSet(UserOrd) val users: userSet.T = UserSet.empty // add users and return them users } }
  98. 121.

    object TwitterClient { import scala.language.existentials def followers(username: String): Set#T =

    { val userSet = new ListSet(UserOrd) val users: userSet.T = UserSet.empty // add users and return them users } }
  99. 123.

    Limitations in SML Q Why aren't modules first-class values in

    Standard ML? A Having types as components of a signature seems to require the notion of dependent types if the language were to support first-class modules. Scala has path-dependent types.
  100. 126.

    Other Differences • SML's modules are not (mutually) recursive, while

    Scala's objects and classes are. • Because objects, traits and classes are values, they're also types in Scala.
  101. 127.

    Other Differences • SML's modules are not (mutually) recursive, while

    Scala's objects and classes are. • Because objects, traits and classes are values, they're also types in Scala. • Objects come with a concept of this (open recursion), SML modules do not.
  102. 128.

    Other Differences • SML's modules are not (mutually) recursive, while

    Scala's objects and classes are. • Because objects, traits and classes are values, they're also types in Scala. • Objects come with a concept of this (open recursion), SML modules do not. • SML modules allow some sort of inheritance, but not overriding, as there's no this.
  103. 132.

    Dependency Injection • Functor params look a lot like constructor

    injection. • The Reader monad is usually advocated by FP people for doing "functional" DI.
  104. 133.

    Dependency Injection • Functor params look a lot like constructor

    injection. • The Reader monad is usually advocated by FP people for doing "functional" DI. • But Reader only injects values, not types.
  105. 134.

    Dependency Injection • Functor params look a lot like constructor

    injection. • The Reader monad is usually advocated by FP people for doing "functional" DI. • But Reader only injects values, not types. • A functor-like approach, i.e., constructor injection, is still useful.
  106. 135.

    Dependency Injection • Functor params look a lot like constructor

    injection. • The Reader monad is usually advocated by FP people for doing "functional" DI. • But Reader only injects values, not types. • A functor-like approach, i.e., constructor injection, is still useful. • Scala alternative: implicit params.
  107. 137.

    Dependency Injection • We should distinguish between: • static dependencies:

    dependencies are known at compile-time. Employ constructor injection or type-classes (coherent implicit params).
  108. 138.

    Dependency Injection • We should distinguish between: • static dependencies:

    dependencies are known at compile-time. Employ constructor injection or type-classes (coherent implicit params). • dynamic dependencies: dependencies are known at runtime. Employ constructor injection, implicit params, Reader monad.
  109. 139.
  110. 140.
  111. 141.