arrow ← to go left, arrow → to go right

Typical DI

dependency injection patterns in Scala
@softprops / ny-scala

we've got

a lot to talk about

di. why do we care?

shared philosophλ

“declare the what

and not the how

fp

“declare the what

and not the how

di

the how

aka tight[1] coupling

class Robot { def perform(x: Int, y: Int) = x * y }
new Robot().perform(2, 10) // 20
1. tight is bad

the what

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
1. loose is good

the why

"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) } }

How does Scala fit in?

Perfectly!

  • Spring
  • Guice
  • you-name-it Java DI library

Cake Typeclasses? maybe[1]...

1 toying with the idea

Let's talk cake

3 ingredients

  • 1 part modules
  • 1 part dependencies
  • 1 part namespace

1. The module

aka 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) }

2. The dependencies

implements the how

trait MultiplyOperation extends ((Int, Int) => Int) { def apply(x: Int, y: Int) = x * y }

3. The namespace

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) } } }

why is this cool?

selfish stackability

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") }

let's talk typeclasses

typeclasses?

Put on your 3-d

type-glasses,

here comes some haskell!

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

Only one instance* of a typeclass for every supported type

*haskell requirement to avoid ambiguity

Typeclasses provide support for adhoc polymorphic interfaces

polymorphism w/o subtyping. Comparable vs Comparator

what does all this have to do with Scala & DI?

a very convenient implementaion

pattern :)

a typeclass

trait TypeClass[GenericType] { def method(param: GenericType): SomeOtherType }
yep. nothing special here.

typeclass instances

object TypeClass { implicit object TypeClassFoo extends TypeClass[Foo] { def method(a: Foo) = // ... } implicit object TypeClassBar extends TypeClass[Bar] { def method(a: Bar) = // ... } }

resolving instances

// 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!") }

questions?

I direct all typeclass questions to Debasish...

j/k :)