Presentation about dependency injection library for Akka actors:
https://github.com/wilaszekg/Scala-Dynamic-DI
Covers the approach details and basics of type-level programming in Scala and Shapeless necessary for understanding
implementation.
ScaLAB conference
Wrocław, 25.06.2016
64. Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
65. Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps = ProxyProps(PriceCalculator.props)
66. Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
67. Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
proxyProps(dependencies)
68. Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
proxyProps(dependencies)
69. Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
val props: Props = proxyProps(dependencies)
70. Starting proxy actor with
Dependencies
val dependencies: Dependencies[Future[Products] :: Future[Promotions] ::
Future[Basket] :: Future[Shop] :: Future[ShopId] :: Future[User] :: HNil]
val proxyProps: ProxyProps[Promotions :: Products :: HNil] =
ProxyProps(PriceCalculator.props)
val props: Props = proxyProps(dependencies)
context.actorOf(props)
71. Proxy behaviour
• runs all dependencies as one Future
• creates target actor once Future completes
• can retry running dependencies Future
• can re-run dependencies once target actor
terminates
72. Benefits of using Shapeless
• Clear and concise API
• Reusable dependencies definitions
• Efficient asynchronous computation - composing
futures
• Compiler ensures presence of all required
dependencies
74. Recursive implicit lookup
trait NotIn[T, L <: HList]
object NotIn {
implicit def notInHNil[T] = new NotIn[T, HNil] {}
implicit def notInList[H, T, L <: HList](implicit tIsNotH: <:!<[T, H],
hIsNotT: <:!<[H, T],
notInTail: NotIn[T, L]) =
new NotIn[T, H :: L] {}
}
75. Recursive implicit lookup
trait NotIn[T, L <: HList]
object NotIn {
implicit def notInHNil[T] = new NotIn[T, HNil] {}
implicit def notInList[H, T, L <: HList](implicit tIsNotH: <:!<[T, H],
hIsNotT: <:!<[H, T],
notInTail: NotIn[T, L]) =
new NotIn[T, H :: L] {}
}
76. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
77. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
addUnique(1 :: "txt" :: HNil, 2)
78. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
79. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
?
80. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
?
81. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
?
?
82. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
?
?
83. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
implicit NotIn[Boolean, HNil]
?
?
?
84. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
implicit NotIn[Boolean, HNil]
?
?
85. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
implicit NotIn[Boolean, HNil]
?
86. Usage of recursive implicit
def addUnique[In <: HList, T](in: In, t: T)(implicit unique: NotIn[T, In]) =
t :: in
addUnique(1 :: "txt" :: HNil, true)
implicit NotIn[Boolean, Int :: String :: HNil]
implicit NotIn[Boolean, String :: HNil]
implicit NotIn[Boolean, HNil]
87. Implicits usage in Shapeless
• Stating facts about types
• Often recursive:
• stop condition
• general condition
• Compiler finds implicit recursively
• Value resolved implicitly may have some functions -
the functions can do something useful for given types
88. More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]
89. More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]
90. More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]
91. More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]
def removeAll[SL <: HList](implicit removeAll : RemoveAll[L, SL])
: removeAll.Out = removeAll(l)
92. More useful operations on
HList
val hList = 1 :: "text" :: List(1, 2, 3) :: HNil
val (removed: String :: Int :: HNil, rest: List[Int] :: HNil) =
hList.removeAll[String :: Int :: HNil]
def removeAll[SL <: HList](implicit removeAll : RemoveAll[L, SL])
: removeAll.Out = removeAll(l)
trait RemoveAll[L <: HList, SL <: HList] {
type Out
def apply(t: L): Out
}
93. Recap
val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
.requires(productsDependency))
context.actorOf(props)
94. Declarative and functional
asynchronous dependencies
val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
.requires(productsDependency))
context.actorOf(props)
95. Type driven composition
val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
.requires(productsDependency))
context.actorOf(props)
96. Seamless futures
composition
val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
.requires(productsDependency))
context.actorOf(props)
97. Compile time checking
val shopDependency = FutureDependency(findShop)
val basketDependency = FutureDependency(findBasket)
val promotionsDependency = FutureDependency(findPromotions)
val productsDependency = ActorDependency(basketKeeper,
AskForProducts(_),
classOf[Products])
val props = ProxyProps(PriceCalculator.props)(Dependencies()
.given(shopId)
.given(user)
.requires(shopDependency)
.requires(basketDependency)
.requires(promotionsDependency)
.requires(productsDependency))
context.actorOf(props)
98. Less boilerplate
class
findShop: ShopId => Future[Shop],
findBasket: (User, Shop) => Future[Basket],
findPromotions: Shop => Future[Promotions],
basketKeeper: ActorRef)
private var
override def
super
for
shop <- findShop(shopId)
basket <- findBasket(user, shop)
promo <- findPromotions(shop)
}
}
}
override def
def
case
basketKeeper !
}
def
case
}
def
case
}
}
class PriceCalculator(products: Products, promotions: Promotions) extends Actor {
override def receive: Receive = {
case Calculate => sender ! promotions.price(products)
}
}