OCaml modules, but in Scala
2 min
Scala 3 是好文明👍
签名
/** Not quite natural numbers :) */
trait Nat {
type A
def zero: A
def succ(a: A): A
def display(a: A): String
}对应
module type Nat = sig
type t
val zero: t
val succ: t -> t
val display: t -> string
end模块
object IntNat extends Nat {
opaque type A = Int
def zero: Int = 0
def succ(a: Int): Int = a + 1
def display(a: Int): String = s"($a : Int)"
}
object TerminalNat extends Nat {
opaque type A = Unit
def zero: Unit = ()
def succ(a: Unit): Unit = ()
def display(a: Unit): String = "()"
}对应
module IntNat: Nat = struct
type t = int
let zero = 0
let succ n = n + 1
let display n = "(" ^ (Int.to_string n) ^ " : int)"
end
module TerminalNat: Nat = struct
type t = unit
let zero = ()
let succ _ = ()
let display _ = "()"
end用一用
@main def main(): Unit = {
import IntNat.*
println(display(succ(succ(zero)))) // (2 : Int)
println(display(succ(zero))) // (1 : Int)
println(display(zero)) // (0 : Int)
}First-class Modules!
我们的 Module 本来就是用 object 实现的,这很 Trivial
import scala.util.chaining.*
@main def main(): Unit = {
def five(n: Nat): n.A = {
import n.*;
succ(succ(succ(succ(succ(zero)))))
}
def print_five(n: Nat): Unit = {
import n.*;
five(n) pipe display pipe println
}
print_five(IntNat) // (5 : Int)
print_five(TerminalNat) // ()
print_five(new Nat {
type A = Boolean
def zero: A = false
def succ(a: A): A = !a
def display(a: A): String = a.toString
}) // true
}First-class Modules,但我不想装箱
等 Scala 3 有个好的单态化/特化方案吧。届时直接改写成
def five[N <: Nat](n: N): n.A
def print_five[N <: Nat](n: N): Unit然后单态化应该就没问题
Functor
final case class NatDisplayPrefixed(prefix: String)(val n: Nat) extends Nat {
type A = n.A
def zero: A = n.zero
def succ(a: A): A = n.succ(a)
def display(a: A): String = prefix + n.display(a)
}
@main def main(): Unit = {
// ...
print_five(NatDisplayPrefixed("value = ")(IntNat)) // value = (5 : Int)
}Dotty 嘛,能耍的花活太多了,这个东西平平无奇