What is Composability
in Scala

@lyrical_logical

Composition

  • Kinds of Composition in Scala
    • Functional Composition
      • ​compose, etc...
    • Objective Composition​​​​
      • e.g. DI, Inheritance, etc...
      • coined word, not a established term sry

Functional Composition

  • FunctionN
val increment: Int => Int = n => n + 1
val double: Int => Int = n => n * 2
val composed = double.compose(increment)
// or increment.andThen(double)

composed(10) // => 22
  • Parser Combinator
object Calculator extends RegexParsers {
  def number = """\d+(\.\d*)?""".r ^^ { _.toDouble }
  def factor = number | "(" ~> expr <~ ")"
  def term = factor ~ rep( "*" ~ factor | "/" ~ factor) ^^ {
    case number ~ list => ...
  }
  def expr = term ~ rep("+" ~ term | "-" ~ term) ^^ {
    case number ~ list => ...
  }
}
  • Functional Composition
    • composite functions as processes
    • ​big function from small functions
    • complex function from trivial functions
    • reduce complexity of functions
  • composability of functions
    • achieved from abstraction
    • abstraction makes codes more trivial
// complex
def complexFunction(s: String, n: Int): Int => Unit
// trivial
def identity[T](t: T): T

someFunction.compose(identity)

Objective Composition

  • Monolithic
class LogRetriever {
  // hard wiring
  val auth = new OAuth2Authorizer
  val logApi = new LogApi
  def retrieve() = {
    auth.autorize match {
      case Success(token) => logApi.getLatests(token)
      case Failure => ...
    }
  }
}
  • Traditional Dependency Injection
class LogRetriever(auth: OAuth2Authorizer, logApi: LogApi) {
  def retrieve() = {
    auth.autorize match {
      case Success(token) => logApi.getLatests(token)
      case Failure => ....
    }
  }
}

new LogRetriever(new OAuth2Authorizer, new LogApi)
  • Traditional Dependency Injection
class LogRetriever(auth: OAuth2Authorizer, logApi: LogApi) {
  def retrieve() = {
    auth.autorize match {
      case Success(token) => logApi.getLatests(token)
      case Failure => ....
    }
  }
}

new LogRetriever(new OAuth2Authorizer, new LogApi)
  • Traditional DI Lacks
    • inject only values and methods, not types
    • if injected objects' signature has a detail
      • exposure details like a `token`
      • type of `token` is public if no need
      • ​implementation hiding is failed
  • DI by self-type annotations and mix-in
class LogRetriever {
  this: {
    type Token
    def authorize(): Token
    def getLatests(token: Token): Seq[String]
  } =>
  def retrieve() = {
   autorize() match {
      case Success(token) => getLatests(token)
      case Failure => ....
    }
  }
}

new LogRetriever extends OAuth2Authorizer with LogApi
  • DI by self-type annotations and mix-in
    • inject types by abstract type member
      • ​token's details are hided
    • self-type annotations as "implemented by"
      • required signatures become more clear
  • Objective Composition
    • composite classes as module
    • ​big module from small modules
    • complex module from trivial module
    • reduce complexity of programs
  • composability of objects
    • achieved from abstraction
    • abstraction makes codes more trivial
    • abstract type member help your codes
      • implementation hiding, etc...
      • like opaque pointer
class SomeClass { this: {
    type T // implementation hiding
  } =>
  // implemented by T
  // but SomeClass doesn't needs know about T
}
  • These techniques are Scala's original?
    • already existed
    • called Functor in ML's module programming
      • ​not subclass of Monad
    • ​Scala's object system can correspond
      • ​ML's module system, wow!

Scala

 

  • object
  • trait
  • abstract type member
  • DI, mixin, function, etc...
    • instantiation methods

ML

  • module
  • signature
  • abstract type
  • functor
    • create modules from an other module

Correspondences

Scala

 

ML

Correspondences(signature)

trait Validator {
  type T
  def validate(t: T): Boolean
}

// normally, use generics in Scala
trait Validator[T] {
  def validate(t: T): Boolean
}
module type Validator = sig
  type t
  val validate: t -> bool
end

Scala

 

ML

Correspondences(module)

object Configuration {
  val credential = ...
  val loggerName = ...
}
module Configuration = struct
  val credential = ...
  val loggerName = ...
end

Scala

 

ML

Correspondences(functor)

trait IntHashMap {
  self: {
    type T
    def hash(n: Int): Int
  } =>

  def get(k: Int): T = ...
  def insert(k: Int, v: T): T = ...
}
// need instantiation
new IntHashMap extends Details
module IntHashSet (
  M: sig
    type t
    val hash: int -> int
  end
) = struct
  let get k = ...
  let insert kv = ...
end
  • Summary
    • Two kinds of compositions in Scala
      • ​Functional, Objective
    • Functional Composition makes
      • ​reduce your code's complexity
    • ​Objective Composition makes
      • ​reduce your program's complexity
      • corresponds ML's module programming
    • ​gain Composability by abstraction

Any questions?

がっこうぐらし!三巻より引用

ScalaMatsuri2017

By りりろじ

ScalaMatsuri2017

  • 4,779