Typical DI
dependency injection patterns in Scala
@softprops / ny-scala
dependency injection patterns in Scala
@softprops / ny-scala
aka tight[1] coupling
class Robot {
def perform(x: Int, y: Int) = x * y
}
new Robot().perform(2, 10) // 20
aka loose[1] coupling
class Robot(op: ((Int, Int) => Int)) {
def perform(x: Int, y: Int) = op(x, y)
}
val multiply = (x: Int, y: Int) => x * y
new Robot(multiply)
.perform(2, 10) // 20
"dependency" should {
"be testable" in {
val multiply = (x: Int, y: Int) => x * y
multiply(2, 10) must be(20)
new Robot(multiply)
.perform(2, 10) must be(20)
}
}
Cake Typeclasses? maybe[1]...
1 toying with the ideaaka the component
declares the what (dependencies)
trait RobotModule {
type Operation = (Int, Int) => Int
def operation: Operation
def perform(x: Int, y: Int) = operation(x, y)
}
implements the how
trait MultiplyOperation
extends ((Int, Int) => Int) {
def apply(x: Int, y: Int) = x * y
}
aka the registry, world, env
binds modules and dependencies
object DefaultRobot extends RobotModule {
// all configuration happens here
def operation: ((Int, Int) => Int) =
new MultiplyOperation {}
}
import DefaultRobot._
perform(2, 4) // 8
perform(8, 2) // 16
trait TestRobot extends RobotModule {
def operation = (x: Int, y: Int) => x + y
}
object RobotSpec extends TestRobot
with Specification {
"robot" should {
"perform" in { perform(4, 2) must be(6) }
}
}
trait IqModule { def iq: Int }
trait Job {
def suitable(iq: Boolean)
def perform(op: (Int => Int) => Int)
}
trait RobotModule {
self: IqModule =>// mv concern to a new module
def perform(j: Job) =
if(j.suitable(iq)) j.perform(operation)
else error("inferior iq error")
}
class Falsy t where
falsy :: t -> Bool
instance Falsy Bool where
falsy False = True
falsy _ = False
instance Falsy Int where
falsy 0 = True
falsy _ = False
instance Falsy [a] where
falsy [] = True
falsy _ = False
instance Falsy (Maybe a) where
falsy Nothing = True
falsy _ = False
How Haskell...
declares a typeclass
class Concept t where
methodA :: signatureA
methodB :: signatureB
Typeclass
Generic Type
Typeclass method signature
How Haskell...
declares an 'instance' of a typeclass
instance Concept Foo where
methodAForTypeFoo
methodBForTypeFoo
Typeclass
Specific Type
Typeclass method signature
*haskell requirement to avoid ambiguity
polymorphism w/o subtyping. Comparable vs Comparator
trait TypeClass[GenericType] {
def method(param: GenericType): SomeOtherType
}
object TypeClass {
implicit object TypeClassFoo
extends TypeClass[Foo] {
def method(a: Foo) = // ...
}
implicit object TypeClassBar
extends TypeClass[Bar] {
def method(a: Bar) = // ...
}
}
// 2.7 implicit param
def typeclassed[T](x: T)(implicit tc: TypeClass[T]) =
tc.method(x)
// 2.8 context bound
def typedclassed[T: TypeClass](x: T) =
implicitly[TypeClass[T]].method(x)
// 2.8 implicit resolver
implicitly[TypeClass[Bar]].method(new Bar)
case study
//kinda like dynamic languages' `falsy` values
trait Falsy[T[_]] {
def apply[A](x: T[A]): Boolean
}
// falsy typeclass instances
object Falsy {
implicit object FalsyList
extends Falsy[List] {
def apply[T](x: List[T]) = x match {
case Nil => true
case _ => false
}
}
// ...
// con't
implicit object FalsyOption extends Falsy[Option] {
def apply[T](x: Option[T]) = x match {
case None => true
case _ => false
}
}
}
object Iffy {
import Falsy._
// falsy control struct
def fif[A[_], B, C](cond: => A[B])(
pass: => C)(
fail: => C)(
implicit f: Falsy[A]): C = {
if(!f(cond)) pass else fail
}
}
import Iffy._
fif(List.empty[Int]) {
println("fail!")
} {
println("falsy!")
}
fif(Some(1)) {
println("not falsy!")
} {
println("fail!")
}
I direct all typeclass questions to Debasish...
j/k :)