14. DUCK
SUBTYPING
CAKE
PATTERN
CHAINING
POLYMORPHISM
When I see a bird that walks like a duck and swims like a duck and quacks
like a duck, I call that bird a duck
class MyClass {
def doSomething(a : type { def run(i : Int) } ) = {
...
a.run(1)
...
}
}
class DuckClass {
def run(i : Int) {
...
}
}
new MyClass().doSomething( new DuckClass() )
Caution: Reflection!
22. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
object Test{
println( Arithmetics.add("Hello", " World") ) // returns “Hello World”
println( Arithmetics.add(2,3) ) // returns 5
// Compile Error, could not find corresponding implicit object
println( Arithmetics.add(123.0, -45.345) )
}
F-BOUNDED
POLYMORPHISM
23. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
object Test{
println( Arithmetics.add("Hello", " World") ) // returns “Hello World”
println( Arithmetics.add(2,3) ) // returns 5
// Compile Error, could not find corresponding implicit object
println( Arithmetics.add(123.0, -45.345) )
}
trait NumericLike[T] {
def plus(x : T, y: T) : T
}
implicit object TInteger extends NumericLike[Int]{
def plus(x : Int, y: Int) : Int = x + y
}
implicit object TString extends NumericLike[String]{
def plus(x : String, y: String) : String = x + y
}
object Arithmetics{
// generalized `add` method
def add[T : NumericLike](x:T, y:T): T = {
val engine = implicitly[NumericLike[T]]
engine.plus(x, y)
}
}
Type Class Pattern
F-BOUNDED
POLYMORPHISM
Type Class can be
treated as an advocate
for specific type T
24. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
RETROACTIVE
POLYMORPHISM
TYPE CLASSES
https://github.com/spray/spray-json
import spray.json._
import DefaultJsonProtocol._
// from json
val source = """{ "some": ["JSON source"] }"""
val ast = source.parseJson
val myObject = ast.convertTo[Map[String, Array[String]]]
// to json
val jsonAst = Map(Array("1"), Array("1")).toJson // to ast
val json = jsonAst.prettyPrint // or .compactPrint
class Color(val name: String, val red: Int, val green: Int, val blue: Int)
// custom serializer via type class
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object ColorJsonFormat extends RootJsonFormat[Color] {
def write(c: Color) = . . .
def read(value: JsValue) = ...
}
}
import MyJsonProtocol._
val json = Color("CadetBlue", 95, 158, 160).toJson
val color = json.convertTo[Color]
The ability to extend functionality of a library without modifying its source is known
as Retroactive Extension
25. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
IMPLICIT DI
RETROACTIVE
POLYMORPHISM
trait Future[+T] extends Awaitable[T] {
…
def filter(pred: T => Boolean) (implicit executor: ExecutionContext):
Future[T] =
map {
r => if (pred(r)) r else throw new
NoSuchElementException("Future.filter predicate is not satisfied")
}
def map[S](f: T => S) (implicit executor: ExecutionContext): Future[S] = {
// transform(f, identity)
val p = Promise[S]()
onComplete { v => p complete (v map f) }
p.future
}
}
TYPE CLASSES
Dependency Injection per
method
26. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
IMPLICIT DI
RETROACTIVE
POLYMORPHISM
trait Future[+T] extends Awaitable[T] {
…
def filter(pred: T => Boolean) (implicit executor: ExecutionContext):
Future[T] =
map {
r => if (pred(r)) r else throw new
NoSuchElementException("Future.filter predicate is not satisfied")
}
def map[S](f: T => S) (implicit executor: ExecutionContext): Future[S] = {
// transform(f, identity)
val p = Promise[S]()
onComplete { v => p complete (v map f) }
p.future
}
}
...
import scala.concurrent.ExecutionContext.Implicits.global
val future = Future { 12345 }. map { x => x + 1} // _ + 1
TYPE CLASSES
Dependency Injection per
method
27. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
IMPLICIT DI
RETROACTIVE
POLYMORPHISM
trait Future[+T] extends Awaitable[T] {
…
def filter(pred: T => Boolean) (implicit executor: ExecutionContext):
Future[T] =
map {
r => if (pred(r)) r else throw new
NoSuchElementException("Future.filter predicate is not satisfied")
}
def map[S](f: T => S) (implicit executor: ExecutionContext): Future[S] = {
// transform(f, identity)
val p = Promise[S]()
onComplete { v => p complete (v map f) }
p.future
}
}
...
import scala.concurrent.ExecutionContext.Implicits.global
val future = Future { 12345 }. map { x => x + 1} // _ + 1
// test code
implicit val executionContext = mock[ExecutionContext]
val future = Future { 12345 }. map { _ + 1}
TYPE CLASSES
Dependency Injection per
method
30. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
TYPE
CONSTRUCTORS
TYPE CLASSES
object Test{
Operations.tupleize(Some(1), Some(2)) // returns Some( (1,2) )
Operations.tupleize(List(1,3), List (2,4)) //returns List( (1,2) , (3,4) )
}
trait ContainerHandler[M[_]] {
def put[A](x: A): M[A]
def get[A](m: M[A]): A
}
object Operations{
def tupleize[M[_]: ContainerHandler, A, B](fst: M[A], snd: M[B]) = {
val engine = implicitly[ContainerHandler[M]]
engine.put( Pair(engine.get(fst), engine.get(snd)) )
}
}
31. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
TYPE
CONSTRUCTORS
TYPE CLASSES
object Test{
Operations.tupleize(Some(1), Some(2)) // returns Some( (1,2) )
Operations.tupleize(List(1,3), List (2,4)) //returns List( (1,2) , (3,4) )
}
trait ContainerHandler[M[_]] {
def put[A](x: A): M[A]
def get[A](m: M[A]): A
}
object Operations{
def tupleize[M[_]: ContainerHandler, A, B](fst: M[A], snd: M[B]) = {
val engine = implicitly[ContainerHandler[M]]
engine.put( Pair(engine.get(fst), engine.get(snd)) )
}
}
implicit val listHandler =
new ContainerHandler[List]{ def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head
}
implicit val optionHandler =
new ContainerHandler[Some]{ def put[A](x: A) = Some(x); def get[A](m: Some[A]) = m.get
}
32. trait Account[T <: Account[T] ] {
def addFunds(amount: BigDecimal): T
}
class CheckingAccount(total: BigDecimal, trxMaxCount: Int)
extends Account[CheckingAccount] {
def addFunds(amount: BigDecimal) : CheckingAccount =
new CheckingAccount(total + amount, trxMaxCount)
}
class SavingAccount(total: BigDecimal) extends Account[SavingAccount] {
def addFunds(amount: BigDecimal) : SavingAccount =
new SavingAccount(total + amount)
}
TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
F-BOUNDED
POLYMORPHISM
TYPE CLASSES
How to define function that, though defined in terms of a supertype, will when
passed a value of some subtype will always return a value of the same subtype as
its argument?
trait Account {
def addFunds(amount: BigDecimal): Account //???
}
33. trait Account[T <: Account[T] ] {
def addFunds(amount: BigDecimal): T
}
class CheckingAccount(total: BigDecimal, trxMaxCount: Int)
extends Account[CheckingAccount] {
def addFunds(amount: BigDecimal) : CheckingAccount =
new CheckingAccount(total + amount, trxMaxCount)
}
class SavingAccount(total: BigDecimal) extends Account[SavingAccount] {
def addFunds(amount: BigDecimal) : SavingAccount =
new SavingAccount(total + amount)
}
TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
F-BOUNDED
POLYMORPHISM
TYPE CLASSES
How to define function that, though defined in terms of a supertype, will when
passed a value of some subtype will always return a value of the same subtype as
its argument?
trait Account {
def addFunds(amount: BigDecimal): Account //???
}
34. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
F-BOUNDED
POLYMORPHISM
TYPE CLASSES
object Account {
val feePercentage = BigDecimal("0.02")
val feeThreshold = BigDecimal("10000.00")
def deposit[T <: Account[T]](amount: BigDecimal, account: T): T = {
if (amount < feeThreshold) account.addFunds(amount - (amount * feePercentage))
else account.addFunds(amount)
}
http://logji.blogspot.com/2012/11/f-bounded-type-polymorphism-give-up-now.html
35. TYPE CLASSES
RETROACTIVE
POLYMORPHISM
IMPLICIT DI
TYPE
CONSTRUCTORS
F-BOUNDED
POLYMORPHISM
F-BOUNDED
POLYMORPHISM
TYPE CLASSES
object Account {
val feePercentage = BigDecimal("0.02")
val feeThreshold = BigDecimal("10000.00")
def deposit[T <: Account[T]](amount: BigDecimal, account: T): T = {
if (amount < feeThreshold) account.addFunds(amount - (amount * feePercentage))
else account.addFunds(amount)
}
def debitAll(amount: BigDecimal, accounts: List[T forSome { type T <: Account[T] }]):
List[T forSome { type T <: Account[T] }] = {
accounts map { _.addFunds(-amount) }
}
}
object Test {
val list = List[T forSome { type T <: Account[T] }](
new CheckingAccount(BigDecimal("0"), 10),
new SavingAccount(BigDecimal("0")))
Account.debitAll(BigDecimal("10.00"), list)
}
The second frame uses
trick with existential
types to store in one
collection
http://logji.blogspot.com/2012/11/f-bounded-type-polymorphism-give-up-now.html
36. Scala Puzzle
def gen2(i: Int): Stream[Int] = Stream(i) append gen2(i+1)
println(gen2(0).take(10).to[List])
// > List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
def gen(i: Int): Stream[Int] = Stream(i) ++ gen(i+1)
println(gen(0).take(10).to[List])
// > List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
(2) The first fails, the second
succeeds
(1) Both will fail with
StackOverflowError
(4) The second fails, the first
succeeds
(3) Both will succeed
1
2
38. TYPE CLASSES
POLYMORPHIC
FUNCTIONS
RETROACTIVE
POLYMORPHISM
We treat type aliases like
abstract virtual functions
passing and returning types, for
instance:
def Prev : Nat
def Plus(A: Nat) : Nat
VIRTUAL TYPES
object Factorial extends App {
trait Nat {
type Prev <: Nat
type Plus[A <: Nat] <: Nat
type Times[A <: Nat] <: Nat
type Factorial <: Nat
}
trait _0 extends Nat {
type Prev = _0
type Plus[A <: Nat] = A
type Times[A <: Nat] = _0
type Factorial = _1
}
trait Succ[N <: Nat] extends Nat {
type This = Succ[N]
type Prev = N
type Times[A <: Nat] = A#Plus[N#Times[A]]
type Plus[A <: Nat] = N#Plus[Succ[A]]
type Factorial = This#Times[Prev#Factorial]
}
type _1 = Succ[_0]
type _2 = Succ[_1]
type _3 = Succ[_2]
type _4 = Succ[_3]
type _5 = Succ[_4]
type _6 = Succ[_5]
type _7 = Succ[_6]
type _8 = Succ[_7]
type _9 = Succ[_8]
// Calculate 3! = 6
implicitly[_3#Factorial =:= _6]
}
https://gist.github.com/mkleen/9290750
39. TYPE CLASSES
POLYMORPHIC
FUNCTIONS
VIRTUAL TYPES
POLYMORPHIC
FUNCTIONS
https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0
Scala function values are monomorphic due to interface Function1[U,V] { .. }
Shapeless library allows to avoid this limitation
Polymorphic functions allow to process heterogeneous collections (HLists)
import poly._
// choose is a function from Sets to Options with no type specific cases
object choose extends (Set ~> Option) {
def apply[T](s : Set[T]) = s.headOption
}
scala> choose(Set(1, 2, 3))
res0: Option[Int] = Some(1)
scala> choose(Set('a', 'b', 'c'))
res1: Option[Char] = Some(a)
scala> val sets = Set(1) :: Set("foo") :: HNil
scala> val opts = sets map choose // map selects cases of choose for each HList element
opts: Option[Int] :: Option[String] :: HNil = Some(1) :: Some(foo) :: HNil
Define polymorphic function
40. TYPE CLASSES
POLYMORPHIC
FUNCTIONS
VIRTUAL TYPES
POLYMORPHIC
FUNCTIONS
https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0
Scala function values are monomorphic due to interface Function1[U,V] { .. }
Shapeless library allows to avoid this limitation
Polymorphic functions allow to process heterogeneous collections (HLists)
import poly._
// choose is a function from Sets to Options with no type specific cases
object choose extends (Set ~> Option) {
def apply[T](s : Set[T]) = s.headOption
}
scala> choose(Set(1, 2, 3))
res0: Option[Int] = Some(1)
scala> choose(Set('a', 'b', 'c'))
res1: Option[Char] = Some(a)
scala> val sets = Set(1) :: Set("foo") :: HNil
scala> val opts = sets map choose // map selects cases of choose for each HList element
opts: Option[Int] :: Option[String] :: HNil = Some(1) :: Some(foo) :: HNil
It behaves like an usual function
Define polymorphic function
41. TYPE CLASSES
POLYMORPHIC
FUNCTIONS
VIRTUAL TYPES
POLYMORPHIC
FUNCTIONS
https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0
Scala function values are monomorphic due to interface Function1[U,V] { .. }
Shapeless library allows to avoid this limitation
Polymorphic functions allow to process heterogeneous collections (HLists)
import poly._
// choose is a function from Sets to Options with no type specific cases
object choose extends (Set ~> Option) {
def apply[T](s : Set[T]) = s.headOption
}
scala> choose(Set(1, 2, 3))
res0: Option[Int] = Some(1)
scala> choose(Set('a', 'b', 'c'))
res1: Option[Char] = Some(a)
scala> val sets = Set(1) :: Set("foo") :: HNil
scala> val opts = sets map choose // map selects cases of choose for each HList element
opts: Option[Int] :: Option[String] :: HNil = Some(1) :: Some(foo) :: HNil
HList runs polymorphic function
It behaves like an usual function
Define polymorphic function