SlideShare une entreprise Scribd logo
1  sur  44
Télécharger pour lire hors ligne
distage
dist✪ge
Modern Staged Dependency Injection for Scala
Modular Functional Programming
with
Context Minimization
through
Garbage Collection
Septimal Mind Ltd
team@7mind.io
1 / 44
distage
The problem: Dependency Injection and Functional Programming
The motivation behind DI pattern and DI frameworks
1. Systems we work with may be represented as graphs. Nodes
are components (usually instances), edges are references,
2. Graph transformation complexity grows with node count
(need to add one constructor parameter, have to modify k
classes), many operations have non-linear complexity,
3. Graph composition has combinatoric complexity (need to run
tests choosing between mock/production repositories and
external APIs, have to write four configurations).
We have several possible options to address these problems:
1. Singletons and Factories: partially solve (2), code still tight
coupled ⇒ expensive tests and refactorings,
2. Service Locator: bit less coupled code, still very expensive,
3. Dependency Injection: less invasive, simplifies isolation but
requires more complex machinery.
2 / 44
distage
The problem: Dependency Injection and Functional Programming
“DI doesn’t compose with FP”: Problems
1. Typical DI framework is OOP oriented and does not support
advanced concepts required for modern FP (typeclasses,
higher-kinded types),
2. Almost all the DI frameworks are working in runtime while
many modern FP concepts are compile-time by their nature,
3. Less guarantees: program which compiles correctly can break
on wiring in runtime. After a huge delay,
4. Wiring is non-determenistic: Guice can spend several minutes
trying to re-instantiate heavy instance multiple times (once
per dependency) then fail,
5. Wiring is opaque: it’s hard or impossible to introspect the
context. E.g. in Guice it’s a real pain to close all the
instantiated Closeables. Adding missing values into the
context (config injections) is not trivial as well.
3 / 44
distage
The problem: Dependency Injection and Functional Programming
“DI doesn’t compose with FP”: Notes
1. We have some compile-time DI frameworks or mechanisms
(see MacWire) allowing us to implement DI as pattern,
2. purely compile-time tools are not convenient: sometimes we
have to deal with purely runtime entities (like plugins and
config values),
3. Graph composition problem is not addressed by any existing
tool.
4 / 44
distage
distage: Staged DI for Scala
distage: how it works
DI implementations are broken. . .
. . . so we may build better one, which must:
1. be well-integrated with type system of our target language
(higher-kinded types, implicits, typeclasses),
2. allow us to introspect and modify our context on the fly,
3. be able to detect as many as possible problems quickly, better
during compilation,
4. still allow us to deal conveniently with runtime entities,
5. give us a way to stop making atomic or conditional contexts.
We made it, it works.
dist✪ge
5 / 44
distage
distage: Staged DI for Scala
distage: how it works
Staged approach
1. Let’s apply Late Binding,
2. let’s collect our context information (bindings) first,
3. then build a DAG representing our context (so-called Project
Network, let’s call it Plan),
4. then analyse this graph for errors (missing references,
conflicts),
5. then apply additional transformations,
6. then interpret the graph.
This is a cornercase of more generic pattern – PPER (Percept,
Plan, Execute, Repeat).
6 / 44
distage
distage: Staged DI for Scala
distage: how it works
Staged approach: outcome
What we get:
1. Planner is pure: it has no side-effects,
2. A plan is a Turing-incomplete program for a simple machine.
It will always terminate in known finite time,
3. An interpreter may perform instantiations at runtime or. . . just
generate Scala code that will do that when compiled,
4. All the job except for instantiations can be done in
compile-time,
5. Interpreter is free to run independent instantiations in parallel,
6. Extremely important: we can transform (rewrite) the plan
before we run iterpreter.
7 / 44
distage
distage: Staged DI for Scala
distage: how it works
Compile-Time and Runtime DI
A Plan:
1 myRepository := create[MyRepository]()
2 myservice := create[MyService](myRepository)
May be interpreted as:
Code tree (compile-time):
1 val myRepository =
2 new MyRepository()
3 val myservice =
4 new MyService(myRepository)
Set of instances (runtime):
1 plan.foldLeft(Context.empty) {
2 case (ctx, op) =>
3 ctx.withInstance(
4 op.key
5 , interpret(action)
6 )
7 }
8 / 44
distage
distage: Staged DI for Scala
distage: how it works
Incomplete plans
This code:
1 class UsersRepoImpl(cassandraCluster: Cluster)
2 extends UsersRepo
3 class UsersService(repository: UsersRepo)
4
5 class UsersModule extends ModuleDef {
6 make[UsersRepo].from[UsersRepoImpl]
7 make[UsersService]
8 }
May produce a plan like:
1 cassandraCluster := import[Cluster]
2 usersRepo: UsersRepo := create[UsersRepoImpl](cassandraCluster)
3 usersService := create[UsersService](usersRepo)
9 / 44
distage
distage: Staged DI for Scala
patterns and Extensions
dist✪ge
Patterns and Extensions
10 / 44
distage
distage: Staged DI for Scala
patterns and Extensions
Pattern: Plan completion
Once we have such a plan:
1 cassandraCluster := import[Cluster]
2 usersRepo: UsersRepo := create[UsersRepoImpl](cassandraCluster)
3 usersService := create[UsersService](usersRepo)
We may add missing values1:
1 val plan = Injector.plan(definitions)
2 val resolved = plan.map {
3 case i: Import if i.is[Cluster] =>
4 val cluster: Cluster = ???
5 Reference(cluster)
6 case op => op
7 }
1
Pseudocode, real API is bit different
11 / 44
distage
distage: Staged DI for Scala
patterns and Extensions
Extension: Configuration Support
distage has HOCON configuration support implemented as an
extension.
1 case class HostPort(host: String, port: Int)
2
3 class HttpServer(@ConfPath("http.listen") listenOn: HostPort) {
4 // ...
5 }
The extension:
1. Takes all the Imports of a Plan,
2. Searches them for a specific @ConfPath annotation,
3. Tries to find corresponding sections in config,
4. Extends plan with config values,
All the config values are resolved even before instantiation of
services ⇒ problems are being shown quickly and all at once.
12 / 44
distage
distage: Staged DI for Scala
patterns and Extensions
Extension: Automatic Sets
1. distage can find all instances type T (like AutoCloseable) in
the context, put them all into a Set[T] then inject that set.
2. ⇒ basic lifecycle support, free of charge.
1 trait Resource {
2 def start(): Unit
3 def stop(): Unit
4 }
5 trait App { def main(): Unit }
6 locator.run { (resources: Set[Resource], app: App) =>
7 try {
8 resources.foreach(_.start())
9 app.main()
10 } finally {
11 resources.foreach(_.close())
12 }
13 }
13 / 44
distage
distage: Staged DI for Scala
patterns and Extensions
The Principle Behind: Feedback Loops
They use so-called Feedback Loops in robotics. . .
14 / 44
distage
distage: Staged DI for Scala
patterns and Extensions
The Principle Behind: PPER Loop1
We found a very generic and important pattern of Feedback Loop
class:
1. Acquire data from the outer world (Percept)
2. Produce a Project Network, Plan. It may be incomplete, but
should allow us to progress (Plan)
◮ Plan is a DAG, actions are nodes, edges are dependencies
3. Execute the Plan (Execute).
◮ Perform the steps of the Plan
◮ Mark your Plan nodes according to the results of their
execution
◮ Let’s call marked plan as Trace
4. Go to step 1 unless termination criteria reached (Repeat)
Let’s call it PPER (pronounced: pepper).
1
Slides: https://goo.gl/okZ8Bw
15 / 44
distage
distage: Staged DI for Scala
Garbage Collector and its Benefits
Garbage Collector and Context Minimization
1. Let’s assume that we have a UsersService and
AccountingService in your context,
2. . . . and we want to write a test for UsersService only,
3. We may exploit staged design and collect the garbage out of
Plan before executing it.
4. We define a garbage collection root, UsersService, and keep
only the operations it transitively depends on. The rest is
being thrown out even before it’s being instantiated,
5. Garbage Collector allows us to compose contexts easier.
16 / 44
distage
distage: Staged DI for Scala
Garbage Collector and its Benefits
Garbage Collector and Context Minimization
17 / 44
distage
distage: Staged DI for Scala
Garbage Collector and its Benefits
Context Minimization for Tests
Context minimization allows us to:
1. Instantiate only the instances which are required for your tests,
2. Save on test startup time (savings may be significant),
3. Save on configuring per-test contexts manually (savings may
be substantial).
18 / 44
distage
distage: Staged DI for Scala
Garbage Collector and its Benefits
Context Minimization for Deployment
Context minimization allows us to:
1. Have one image with all our software components (Roles1),
2. . . . keeping development flows of these components isolated,
3. Decide which components we want to run when we start the
image,
4. Have higher computational density
5. substantially simplify development flows: we may run full
environment with a single command on a low-end machine,
6. Fuse Microservices with Monoliths keeping all their benefits.
1 server1# docker run -ti company/product +analytics
2 server2# docker run -ti company/product +accounting +users
3 laptop# docker run -ti company/product --run-everything
1
Slides: https://goo.gl/iaMt43
19 / 44
distage
distage: Staged DI for Scala
Scala Typesystem Integration: Fusional Programming
dist✪ge
Scala Typesystem Integration:
Fusional Programming
20 / 44
distage
distage: Staged DI for Scala
Scala Typesystem Integration: Fusional Programming
Kind-Polymorphic Type Tags
◮ distage uses types
as lookup keys
◮ But scalac has
TypeTags only for
kind "*"!
◮ distage added
any-kinded ‘Tag‘s
◮ Modules can be
polymorphic on types
of any kind
1 def m1[F[_]: TagK: Monad, A: Encoder]
2 = new ModuleDef {
3 make[SendJson[F, A]]
4 addImplicit[Encoder[A]]
5 addImplicit[Monad[F]]
6 make[Network[F]].from(...) }
7 // `Tag` implicit accepts
8 // type parameters of arbitrary kind
9 def mTrans[FT[_[_], _]: Tag.auto.T] =
10 m1[FT[IO], AccountReport]
11 mTrans[OptionT]
1 type TagK[F[_]] = HKTag[{ type Arg[A] = F[A] }]
2 type TagKK[F[_, _]] = HKTag[{ type Arg[A, B] = F[A, B] }]
3 type Tag.auto.T[F[...]] = HKTag[{ type Arg[...] = F[...] }]
21 / 44
distage
distage: Staged DI for Scala
Scala Typesystem Integration: Fusional Programming
Typeclass instance injection (Implicit Injection)
1. distage wires all parameters, inlcuding implicits
2. Leaving implicits solely to implicit resolution would’ve been
unmodular, because we want to bind instances late, not at
definition time
3. Therefore implicits should be declared explicitly
4. Tagless-final style is encouraged – add implicit parameters,
instead of importing concrete implicits!
1 def polyModule[F[_]: Sync: TagK] = new ModuleDef {
2 make[Sync[F]].from(implicitly[Sync[F]])
3 make[F[Unit]].named("hello").from(
4 S: Sync[F] => S.delay(println("Hello World!")))
5 }
6 Injector()
7 .produce(polyModule[IO])
8 .get[IO[Unit]]("hello").unsafeRunSync()
22 / 44
distage
distage: Staged DI for Scala
Scala Typesystem Integration: Fusional Programming
Code example: Tagless Final Style
1 trait Interaction[F[_]] {
2 def say(msg: String): F[Unit]
3 def ask(prompt: String): F[String] }
4
5 class TaglessHelloWorld[F[_]: Monad: Interaction] {
6 val program = for {
7 userName <- Interaction[F].ask("What's your name?")
8 _ <- Interaction[F].say(s"Hello $userName!")
9 } yield () }
10
11 def wiring[F[_]: TagK: Sync] = new ModuleDef {
12 make[TaglessHelloWorld[F]]
13 make[Interaction[F]].from(new Interaction[F] {...})
14 addImplicit[Monad[F]] }
15
16 Injector().produce(wiring[IO]).get[TaglessHelloWorld[IO]]
17 .program.unsafeRunSync()
23 / 44
distage
distage: Staged DI for Scala
Scala Typesystem Integration: Fusional Programming
Code example: Scalaz ZIO Injection
1 trait MonadFilesystem[F[_, _]] {
2 def readFile(fileName: String): F[Exception, String]
3 }
4
5 class ZioFilesystemModule extends ModuleDef {
6 make[MonadFilesystem[IO]].from(new MonadFilesystem[IO] {
7 def readFile(fileName: String) =
8 IO.syncException(Source.fromFile(fileName)("UTF-8")
9 .mkString)
10 })
11 make[GetCpus[IO]]
12 }
13
14 class GetCpus(fs: MonadFilesystem[IO]) {
15 def apply(): IO[Exception, Int] =
16 fs.readFile("/proc/cpuinfo").flatMap(...)
17 }
24 / 44
distage
distage: Staged DI for Scala
Scala Typesystem Integration: Fusional Programming
Lambda Injection, ProviderMagnet
1. We can bind functions as constructors
2. Arbitrary-arity functions can run with arguments from Locator
3. Put annotations on types to summon named instances or
config values (from distage-config)
1 case class A()
2 case class B(a: A, default: Int = 5)
3 val ctx: Locator = Injector(config).produce(new ModuleDef {
4 make[A].from(A())
5 make[B].from{ a: A => B(a) }
6 })
7 case class C(a: A, b: B, host: HostPort)
8 val c: C = ctx.run {
9 (a: A, b: B, host: HostPort @ConfPath("http.listen")) =>
10 C(a, b, host)
11 }
25 / 44
distage
distage: Staged DI for Scala
Scala Typesystem Integration: Fusional Programming
Path-dependent types
1. Path-dependent types and projections are supported
2. Path-prefix should be wired for the child type to be wired
1 trait Cake { trait Child }
2
3 val cakeModule = new ModuleDef {
4 make[Cake]
5 make[Cake#Child]
6 }
7 val cake = Injector().produce(cakeModule).get[Cake]
8
9 val instanceCakeModule = new ModuleDef {
10 make[cake.type].from[cake.type](cake: cake.type)
11 make[cake.Child]
12 }
13 val cakeChild = Injector().produce(instanceCakeModule)
14 .get[cake.Child]
26 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
dist✪ge
Features to boost productivity
27 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Dynamic Plugins
Just drop your modules into your classpath:
1 class AccountingModule extends PluginDef {
2 make[AccountingService].from[AccountingServiceImpl]
3 // ...
4 }
Then you may pick up all the modules and build your context:
1 val plugins = new PluginLoaderDefaultImpl(
2 PluginConfig(Seq("com.company.plugins"))
3 ).load()
4 // ... pass to an Injector
1. Useful while you are prototyping your app,
2. In maintenance phase you may switch to static configuration.
28 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Dynamic Testkit (ScalaTest)
1 class AccountingServiceTest extends DistagePluginSpec {
2 "accounting service" must {
3 "be resolved dynamically" in di {
4 (acc: AccountingService) =>
5 assert(acc.getBalance("bad-user") == 0)
6 }
7 }
8 }
1. You don’t need to setup your context, it’s done automatically
by Plugin Loader and Garbage Collector,
2. And it takes just milliseconds, not like in Spring,
3. Garbage collection roots are inferred from test’s signature,
4. Only the things required for a particular test are being
instantiated.
29 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Tags
1. Each binding may be marked with a tag,
2. Some bindings may be excluded from the context before
planning by a predicate,
3. This is unsafe but convenient way to reconfigure contexts
conditionally.
1 class ProductionPlugin extends PluginDef {
2 tag("production", "cassandra")
3 make[UserRepo].from[CassandraUserRepo]
4 }
5 class MockPlugin extends PluginDef {
6 tag("test", "mock")
7 make[UserRepo].from[MockUserRepo]
8 }
9 // ...
10 val disabledTags = t"mock" && t"dummy"
11 val plan = injector.plan(definition.filter(disabledTags))
30 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Circular dependencies
1. Supported, Proxy concept used,
2. By-name parameters (class C(param: => P)) supported, no
runtime code-generation requried,
3. Other cases are supported as well with runtime
code-generation,
4. Limitations: typical. You cannot use an injected parameter
immediately in a constructor, you cannot have circular
non-by-name dependencies with final classes,
5. Circular dependency resolution is optional, you may turn it off.
31 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Plan Introspection: example context
1 class Cluster
2 trait UsersService
3 trait AccountingService
4 trait UserRepo
5 trait AccountsRepo
6
7 class UserRepoImpl(cluster: Cluster) extends UserRepo
8 class AccountsRepoImpl(cluster: Cluster) extends AccountsRepo
9 class UserServiceImpl(userRepo: UserRepo) extends UsersService
10 class AccountingServiceImpl(accountsRepo: AccountsRepo)
11 extends AccountingService
12
13 class UsersApiImpl(service: UsersService
14 , accountsApi: AccountsApiImpl)
15 class AccountsApiImpl(service: AccountingService
16 , usersApi: UsersApiImpl) // circular dependency
17 class App(uapi: UsersApiImpl, aapi: AccountsApiImpl)
32 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Plan Introspection: example bindings1
1 val definition = new ModuleDef {
2 make[Cluster]
3 make[UserRepo].from[UserRepoImpl]
4 make[AccountsRepo].from[AccountsRepoImpl]
5 make[UsersService].from[UserServiceImpl]
6 make[AccountingService].from[AccountingServiceImpl]
7 make[UsersApiImpl]
8 make[AccountsApiImpl]
9 make[App]
10 }
11 val injector = Injector()
12 val plan = injector.plan(definition)
1
Full code example: https://goo.gl/7ZwHfX
33 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Plan Introspection: plan dumps
1 println(plan.render) // look for the circular dependency!
34 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Plan Introspection: dependency trees
You may explore dependencies of a component:
1 val dependencies = plan.topology.dependencies
2 println(dependencies.tree(DIKey.get[AccountsApiImpl]))
Circular dependencies are specifically marked.
35 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Trait Completion
1 trait UsersService {
2 protected def repository: UsersRepo
3 def add(user: User): Unit = {
4 repository.put(user.id, user)
5 ???
6 }
7 }
We may bind this trait directly, without an implementation class:
1 make[UsersService]
1. Corresponding class will be generated by distage,
2. Null-arg abstract methods will be wired with context values,
3. Works in both runtime and compile-time.
36 / 44
distage
distage: Staged DI for Scala
Features to boost productivity
Factory Methods (Assisted Injection)
1 class UserActor(sessionId: UUID, sessionRepo: SessionRepo)
2
3 trait ActorFactory {
4 def createActor(sessionId: UUID): UserActor
5 }
1. createActor is a factory method,
2. createActor will be generated by distage,
3. non-null-arg abstract methods are treated as factory methods,
4. Non-invasive assisted injection: sessionId: UUID will be taken
from method parameter, sessionRepo: SessionRepo will be
wired from context,
5. Useful for Akka, lot more convenient than Guice,
6. Works in both runtime and compile-time.
37 / 44
distage
distage: Staged DI for Scala
7mind Stack
dist✪ge
7mind Stack
38 / 44
distage
distage: Staged DI for Scala
7mind Stack
distage: status and things to do
distage is:
1. ready to use,
2. in real production,
3. all Runtime APIs are available,
4. Compile-time verification, trait completion, assisted injections
and lambda injections are available.
Our plans:
1. Refactor Roles API,
2. Support running Producer within a monad (to use with
Scalaz ZIO, Monix, cats-effect, etc),
3. Support Scala.js,
4. Support optional isolated classloaders (in foreseeable future),
5. Publish compile-time Producer,
6. Check our GitHub: https://github.com/pshirshov/izumi-r2.
39 / 44
distage
distage: Staged DI for Scala
7mind Stack
distage is just a part of our stack
We have a vision backed by our tools:
1. Idealingua: transport and codec agnostic gRPC alternative
with rich modeling language,
2. LogStage: zero-cost logging framework,
3. Fusional Programming and Design guidelines. We love both
FP and OOP,
4. Continous Delivery guidelines for Role-based process,
5. Percept-Plan-Execute Generative Programming approach,
abstract machine and computational model. Addresses
Project Planning (see Operations Research). Examples:
orchestration, build systems.
Altogether these things already allowed us to significantly reduce
development costs and delivery time for our client.
More slides to follow. 40 / 44
distage
distage: Staged DI for Scala
7mind Stack
You use Guice?
Switch to distage!
41 / 44
distage
distage: Staged DI for Scala
7mind Stack
Teaser: LogStage
A log call . . .
1 log.info(s"$user logged in with $sessionId!")
. . . may be rendered as a text like 17:05:18 UserService.login
user=John Doe logged in with sessionId=DEADBEEF!
. . . or a structured JSON:
1 {
2 "user": "John Doe",
3 "sessionId": "DEADBEEF",
4 "_template": "$user logged in with $sessionId!",
5 "_location": "UserService.scala:265",
6 "_context": "UserService.login",
7 }
42 / 44
distage
distage: Staged DI for Scala
7mind Stack
Teaser: Idealingua
1 id UserId { uid: str }
2 data User { name: str /* ... more fields */ }
3 data PublicUser {
4 + InternalUser
5 - SecurityAttributes
6 }
7 adt Failure = NotFound | UnknownFailure
8 service Service {
9 def getUser(id: UserId): User !! Failure
10 }
1. Convenient Data and Interface Definition Language,
2. Extensible, transport-agnostic, abstracted from wire format,
3. JSON + HTTP / WebSocket at the moment,
4. C#, go, Scala, TypeScript at the moment,
5. Better than gRPC/ REST / Swagger/ etc.
43 / 44
distage
distage: Staged DI for Scala
7mind Stack
Thank you for your attention
distage website: https://izumi.7mind.io/
We’re looking for clients, contributors, adopters and colleagues ;)
About the author:
1. coding for 18 years, 10 years of hands-on commercial
engineering experience,
2. has been leading a cluster orchestration team in Yandex, “the
Russian Google”,
3. implemented “Interstellar Spaceship” – an orchestration
solution to manage 50K+ physical machines across 6
datacenters,
4. Owns an Irish R&D company, https://7mind.io,
5. Contact: team@7mind.io,
6. Github: https://github.com/pshirshov
7. Download slides: https://github.com/7mind/slides/
44 / 44

Contenu connexe

Tendances

Java 9 New Features
Java 9 New FeaturesJava 9 New Features
Java 9 New FeaturesAli BAKAN
 
Connect2017 DEV-1550 Why Java 8? Or, What's a Lambda?
Connect2017 DEV-1550 Why Java 8? Or, What's a Lambda?Connect2017 DEV-1550 Why Java 8? Or, What's a Lambda?
Connect2017 DEV-1550 Why Java 8? Or, What's a Lambda?Julian Robichaux
 
[AnDevCon 2016] Mutation Testing for Android
[AnDevCon 2016] Mutation Testing for Android[AnDevCon 2016] Mutation Testing for Android
[AnDevCon 2016] Mutation Testing for AndroidHazem Saleh
 
Efficient JavaScript Unit Testing, JavaOne China 2013
Efficient JavaScript Unit Testing, JavaOne China 2013Efficient JavaScript Unit Testing, JavaOne China 2013
Efficient JavaScript Unit Testing, JavaOne China 2013Hazem Saleh
 
JavaFX JumpStart @JavaOne 2016
JavaFX JumpStart @JavaOne 2016JavaFX JumpStart @JavaOne 2016
JavaFX JumpStart @JavaOne 2016Hendrik Ebbers
 
Testing with JUnit 5 and Spring
Testing with JUnit 5 and SpringTesting with JUnit 5 and Spring
Testing with JUnit 5 and SpringVMware Tanzu
 
Managed Beans: When, Why and How
Managed Beans: When, Why and HowManaged Beans: When, Why and How
Managed Beans: When, Why and HowRussell Maher
 
Testing ansible roles with molecule
Testing ansible roles with moleculeTesting ansible roles with molecule
Testing ansible roles with moleculeWerner Dijkerman
 
Devoxx17 - Préparez-vous à la modularité selon Java 9
Devoxx17 - Préparez-vous à la modularité selon Java 9Devoxx17 - Préparez-vous à la modularité selon Java 9
Devoxx17 - Préparez-vous à la modularité selon Java 9Alexis Hassler
 
How to setup unit testing in Android Studio
How to setup unit testing in Android StudioHow to setup unit testing in Android Studio
How to setup unit testing in Android Studiotobiaspreuss
 
Sling IDE Tooling @ adaptTo 2013
Sling IDE Tooling @ adaptTo 2013Sling IDE Tooling @ adaptTo 2013
Sling IDE Tooling @ adaptTo 2013Robert Munteanu
 
Jenkins Evolutions - JEEConf 2012
Jenkins Evolutions - JEEConf 2012Jenkins Evolutions - JEEConf 2012
Jenkins Evolutions - JEEConf 2012Anton Arhipov
 
Idiomatic Gradle Plugin Writing - GradleSummit 2016
Idiomatic Gradle Plugin Writing - GradleSummit 2016Idiomatic Gradle Plugin Writing - GradleSummit 2016
Idiomatic Gradle Plugin Writing - GradleSummit 2016Schalk Cronjé
 
Load Testing with k6 framework
Load Testing with k6 frameworkLoad Testing with k6 framework
Load Testing with k6 frameworkSvetlin Nakov
 
Unit Test Android Without Going Bald
Unit Test Android Without Going BaldUnit Test Android Without Going Bald
Unit Test Android Without Going BaldDavid Carver
 
Will it blend? Java agents and OSGi
Will it blend? Java agents and OSGiWill it blend? Java agents and OSGi
Will it blend? Java agents and OSGiRobert Munteanu
 
Architecting your GWT applications with GWT-Platform - Lesson 02
Architecting your GWT applications with GWT-Platform - Lesson 02Architecting your GWT applications with GWT-Platform - Lesson 02
Architecting your GWT applications with GWT-Platform - Lesson 02rhemsolutions
 
The Web on OSGi: Here's How
The Web on OSGi: Here's HowThe Web on OSGi: Here's How
The Web on OSGi: Here's Howmrdon
 

Tendances (20)

Java 9 New Features
Java 9 New FeaturesJava 9 New Features
Java 9 New Features
 
Connect2017 DEV-1550 Why Java 8? Or, What's a Lambda?
Connect2017 DEV-1550 Why Java 8? Or, What's a Lambda?Connect2017 DEV-1550 Why Java 8? Or, What's a Lambda?
Connect2017 DEV-1550 Why Java 8? Or, What's a Lambda?
 
[AnDevCon 2016] Mutation Testing for Android
[AnDevCon 2016] Mutation Testing for Android[AnDevCon 2016] Mutation Testing for Android
[AnDevCon 2016] Mutation Testing for Android
 
Efficient JavaScript Unit Testing, JavaOne China 2013
Efficient JavaScript Unit Testing, JavaOne China 2013Efficient JavaScript Unit Testing, JavaOne China 2013
Efficient JavaScript Unit Testing, JavaOne China 2013
 
JavaFX JumpStart @JavaOne 2016
JavaFX JumpStart @JavaOne 2016JavaFX JumpStart @JavaOne 2016
JavaFX JumpStart @JavaOne 2016
 
Testing with JUnit 5 and Spring
Testing with JUnit 5 and SpringTesting with JUnit 5 and Spring
Testing with JUnit 5 and Spring
 
Managed Beans: When, Why and How
Managed Beans: When, Why and HowManaged Beans: When, Why and How
Managed Beans: When, Why and How
 
Testing ansible roles with molecule
Testing ansible roles with moleculeTesting ansible roles with molecule
Testing ansible roles with molecule
 
Devoxx17 - Préparez-vous à la modularité selon Java 9
Devoxx17 - Préparez-vous à la modularité selon Java 9Devoxx17 - Préparez-vous à la modularité selon Java 9
Devoxx17 - Préparez-vous à la modularité selon Java 9
 
How to setup unit testing in Android Studio
How to setup unit testing in Android StudioHow to setup unit testing in Android Studio
How to setup unit testing in Android Studio
 
Sling IDE Tooling
Sling IDE ToolingSling IDE Tooling
Sling IDE Tooling
 
Sling IDE Tooling @ adaptTo 2013
Sling IDE Tooling @ adaptTo 2013Sling IDE Tooling @ adaptTo 2013
Sling IDE Tooling @ adaptTo 2013
 
Jenkins Evolutions - JEEConf 2012
Jenkins Evolutions - JEEConf 2012Jenkins Evolutions - JEEConf 2012
Jenkins Evolutions - JEEConf 2012
 
Idiomatic Gradle Plugin Writing - GradleSummit 2016
Idiomatic Gradle Plugin Writing - GradleSummit 2016Idiomatic Gradle Plugin Writing - GradleSummit 2016
Idiomatic Gradle Plugin Writing - GradleSummit 2016
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Load Testing with k6 framework
Load Testing with k6 frameworkLoad Testing with k6 framework
Load Testing with k6 framework
 
Unit Test Android Without Going Bald
Unit Test Android Without Going BaldUnit Test Android Without Going Bald
Unit Test Android Without Going Bald
 
Will it blend? Java agents and OSGi
Will it blend? Java agents and OSGiWill it blend? Java agents and OSGi
Will it blend? Java agents and OSGi
 
Architecting your GWT applications with GWT-Platform - Lesson 02
Architecting your GWT applications with GWT-Platform - Lesson 02Architecting your GWT applications with GWT-Platform - Lesson 02
Architecting your GWT applications with GWT-Platform - Lesson 02
 
The Web on OSGi: Here's How
The Web on OSGi: Here's HowThe Web on OSGi: Here's How
The Web on OSGi: Here's How
 

Similaire à distage: Purely Functional Staged Dependency Injection; bonus: Faking Kind Polymorphism in Scala 2

Advanced Node.JS Meetup
Advanced Node.JS MeetupAdvanced Node.JS Meetup
Advanced Node.JS MeetupLINAGORA
 
Distributed tracing 101
Distributed tracing 101Distributed tracing 101
Distributed tracing 101Itiel Shwartz
 
Go Faster With Native Compilation
Go Faster With Native CompilationGo Faster With Native Compilation
Go Faster With Native CompilationPGConf APAC
 
Go faster with_native_compilation Part-2
Go faster with_native_compilation Part-2Go faster with_native_compilation Part-2
Go faster with_native_compilation Part-2Rajeev Rastogi (KRR)
 
What is Java Technology (An introduction with comparision of .net coding)
What is Java Technology (An introduction with comparision of .net coding)What is Java Technology (An introduction with comparision of .net coding)
What is Java Technology (An introduction with comparision of .net coding)Shaharyar khan
 
Nt1310 Unit 3 Language Analysis
Nt1310 Unit 3 Language AnalysisNt1310 Unit 3 Language Analysis
Nt1310 Unit 3 Language AnalysisNicole Gomez
 
Linux Assignment 3
Linux Assignment 3Linux Assignment 3
Linux Assignment 3Diane Allen
 
Spring, Functions, Serverless and You
Spring, Functions, Serverless and YouSpring, Functions, Serverless and You
Spring, Functions, Serverless and YouVMware Tanzu
 
Collaborative Terraform with Atlantis
Collaborative Terraform with AtlantisCollaborative Terraform with Atlantis
Collaborative Terraform with AtlantisFerenc Kovács
 
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)Ovidiu Farauanu
 

Similaire à distage: Purely Functional Staged Dependency Injection; bonus: Faking Kind Polymorphism in Scala 2 (20)

Surge2012
Surge2012Surge2012
Surge2012
 
Spring boot
Spring bootSpring boot
Spring boot
 
Data herding
Data herdingData herding
Data herding
 
Data herding
Data herdingData herding
Data herding
 
Advanced Node.JS Meetup
Advanced Node.JS MeetupAdvanced Node.JS Meetup
Advanced Node.JS Meetup
 
Distributed Tracing
Distributed TracingDistributed Tracing
Distributed Tracing
 
Distributed tracing 101
Distributed tracing 101Distributed tracing 101
Distributed tracing 101
 
Java multi thread programming on cmp system
Java multi thread programming on cmp systemJava multi thread programming on cmp system
Java multi thread programming on cmp system
 
Go Faster With Native Compilation
Go Faster With Native CompilationGo Faster With Native Compilation
Go Faster With Native Compilation
 
Go faster with_native_compilation Part-2
Go faster with_native_compilation Part-2Go faster with_native_compilation Part-2
Go faster with_native_compilation Part-2
 
What is Java Technology (An introduction with comparision of .net coding)
What is Java Technology (An introduction with comparision of .net coding)What is Java Technology (An introduction with comparision of .net coding)
What is Java Technology (An introduction with comparision of .net coding)
 
Nt1310 Unit 3 Language Analysis
Nt1310 Unit 3 Language AnalysisNt1310 Unit 3 Language Analysis
Nt1310 Unit 3 Language Analysis
 
Embedded multiple choice questions
Embedded multiple choice questionsEmbedded multiple choice questions
Embedded multiple choice questions
 
Java 8 Overview
Java 8 OverviewJava 8 Overview
Java 8 Overview
 
Readme
ReadmeReadme
Readme
 
Linux Assignment 3
Linux Assignment 3Linux Assignment 3
Linux Assignment 3
 
Spring, Functions, Serverless and You
Spring, Functions, Serverless and YouSpring, Functions, Serverless and You
Spring, Functions, Serverless and You
 
Collaborative Terraform with Atlantis
Collaborative Terraform with AtlantisCollaborative Terraform with Atlantis
Collaborative Terraform with Atlantis
 
Go Faster With Native Compilation
Go Faster With Native CompilationGo Faster With Native Compilation
Go Faster With Native Compilation
 
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
Functional Patterns for C++ Multithreading (C++ Dev Meetup Iasi)
 

Dernier

MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based projectAnoyGreter
 
Best Web Development Agency- Idiosys USA.pdf
Best Web Development Agency- Idiosys USA.pdfBest Web Development Agency- Idiosys USA.pdf
Best Web Development Agency- Idiosys USA.pdfIdiosysTechnologies1
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....kzayra69
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commercemanigoyal112
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 

Dernier (20)

MYjobs Presentation Django-based project
MYjobs Presentation Django-based projectMYjobs Presentation Django-based project
MYjobs Presentation Django-based project
 
Best Web Development Agency- Idiosys USA.pdf
Best Web Development Agency- Idiosys USA.pdfBest Web Development Agency- Idiosys USA.pdf
Best Web Development Agency- Idiosys USA.pdf
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commerce
 
2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 

distage: Purely Functional Staged Dependency Injection; bonus: Faking Kind Polymorphism in Scala 2

  • 1. distage dist✪ge Modern Staged Dependency Injection for Scala Modular Functional Programming with Context Minimization through Garbage Collection Septimal Mind Ltd team@7mind.io 1 / 44
  • 2. distage The problem: Dependency Injection and Functional Programming The motivation behind DI pattern and DI frameworks 1. Systems we work with may be represented as graphs. Nodes are components (usually instances), edges are references, 2. Graph transformation complexity grows with node count (need to add one constructor parameter, have to modify k classes), many operations have non-linear complexity, 3. Graph composition has combinatoric complexity (need to run tests choosing between mock/production repositories and external APIs, have to write four configurations). We have several possible options to address these problems: 1. Singletons and Factories: partially solve (2), code still tight coupled ⇒ expensive tests and refactorings, 2. Service Locator: bit less coupled code, still very expensive, 3. Dependency Injection: less invasive, simplifies isolation but requires more complex machinery. 2 / 44
  • 3. distage The problem: Dependency Injection and Functional Programming “DI doesn’t compose with FP”: Problems 1. Typical DI framework is OOP oriented and does not support advanced concepts required for modern FP (typeclasses, higher-kinded types), 2. Almost all the DI frameworks are working in runtime while many modern FP concepts are compile-time by their nature, 3. Less guarantees: program which compiles correctly can break on wiring in runtime. After a huge delay, 4. Wiring is non-determenistic: Guice can spend several minutes trying to re-instantiate heavy instance multiple times (once per dependency) then fail, 5. Wiring is opaque: it’s hard or impossible to introspect the context. E.g. in Guice it’s a real pain to close all the instantiated Closeables. Adding missing values into the context (config injections) is not trivial as well. 3 / 44
  • 4. distage The problem: Dependency Injection and Functional Programming “DI doesn’t compose with FP”: Notes 1. We have some compile-time DI frameworks or mechanisms (see MacWire) allowing us to implement DI as pattern, 2. purely compile-time tools are not convenient: sometimes we have to deal with purely runtime entities (like plugins and config values), 3. Graph composition problem is not addressed by any existing tool. 4 / 44
  • 5. distage distage: Staged DI for Scala distage: how it works DI implementations are broken. . . . . . so we may build better one, which must: 1. be well-integrated with type system of our target language (higher-kinded types, implicits, typeclasses), 2. allow us to introspect and modify our context on the fly, 3. be able to detect as many as possible problems quickly, better during compilation, 4. still allow us to deal conveniently with runtime entities, 5. give us a way to stop making atomic or conditional contexts. We made it, it works. dist✪ge 5 / 44
  • 6. distage distage: Staged DI for Scala distage: how it works Staged approach 1. Let’s apply Late Binding, 2. let’s collect our context information (bindings) first, 3. then build a DAG representing our context (so-called Project Network, let’s call it Plan), 4. then analyse this graph for errors (missing references, conflicts), 5. then apply additional transformations, 6. then interpret the graph. This is a cornercase of more generic pattern – PPER (Percept, Plan, Execute, Repeat). 6 / 44
  • 7. distage distage: Staged DI for Scala distage: how it works Staged approach: outcome What we get: 1. Planner is pure: it has no side-effects, 2. A plan is a Turing-incomplete program for a simple machine. It will always terminate in known finite time, 3. An interpreter may perform instantiations at runtime or. . . just generate Scala code that will do that when compiled, 4. All the job except for instantiations can be done in compile-time, 5. Interpreter is free to run independent instantiations in parallel, 6. Extremely important: we can transform (rewrite) the plan before we run iterpreter. 7 / 44
  • 8. distage distage: Staged DI for Scala distage: how it works Compile-Time and Runtime DI A Plan: 1 myRepository := create[MyRepository]() 2 myservice := create[MyService](myRepository) May be interpreted as: Code tree (compile-time): 1 val myRepository = 2 new MyRepository() 3 val myservice = 4 new MyService(myRepository) Set of instances (runtime): 1 plan.foldLeft(Context.empty) { 2 case (ctx, op) => 3 ctx.withInstance( 4 op.key 5 , interpret(action) 6 ) 7 } 8 / 44
  • 9. distage distage: Staged DI for Scala distage: how it works Incomplete plans This code: 1 class UsersRepoImpl(cassandraCluster: Cluster) 2 extends UsersRepo 3 class UsersService(repository: UsersRepo) 4 5 class UsersModule extends ModuleDef { 6 make[UsersRepo].from[UsersRepoImpl] 7 make[UsersService] 8 } May produce a plan like: 1 cassandraCluster := import[Cluster] 2 usersRepo: UsersRepo := create[UsersRepoImpl](cassandraCluster) 3 usersService := create[UsersService](usersRepo) 9 / 44
  • 10. distage distage: Staged DI for Scala patterns and Extensions dist✪ge Patterns and Extensions 10 / 44
  • 11. distage distage: Staged DI for Scala patterns and Extensions Pattern: Plan completion Once we have such a plan: 1 cassandraCluster := import[Cluster] 2 usersRepo: UsersRepo := create[UsersRepoImpl](cassandraCluster) 3 usersService := create[UsersService](usersRepo) We may add missing values1: 1 val plan = Injector.plan(definitions) 2 val resolved = plan.map { 3 case i: Import if i.is[Cluster] => 4 val cluster: Cluster = ??? 5 Reference(cluster) 6 case op => op 7 } 1 Pseudocode, real API is bit different 11 / 44
  • 12. distage distage: Staged DI for Scala patterns and Extensions Extension: Configuration Support distage has HOCON configuration support implemented as an extension. 1 case class HostPort(host: String, port: Int) 2 3 class HttpServer(@ConfPath("http.listen") listenOn: HostPort) { 4 // ... 5 } The extension: 1. Takes all the Imports of a Plan, 2. Searches them for a specific @ConfPath annotation, 3. Tries to find corresponding sections in config, 4. Extends plan with config values, All the config values are resolved even before instantiation of services ⇒ problems are being shown quickly and all at once. 12 / 44
  • 13. distage distage: Staged DI for Scala patterns and Extensions Extension: Automatic Sets 1. distage can find all instances type T (like AutoCloseable) in the context, put them all into a Set[T] then inject that set. 2. ⇒ basic lifecycle support, free of charge. 1 trait Resource { 2 def start(): Unit 3 def stop(): Unit 4 } 5 trait App { def main(): Unit } 6 locator.run { (resources: Set[Resource], app: App) => 7 try { 8 resources.foreach(_.start()) 9 app.main() 10 } finally { 11 resources.foreach(_.close()) 12 } 13 } 13 / 44
  • 14. distage distage: Staged DI for Scala patterns and Extensions The Principle Behind: Feedback Loops They use so-called Feedback Loops in robotics. . . 14 / 44
  • 15. distage distage: Staged DI for Scala patterns and Extensions The Principle Behind: PPER Loop1 We found a very generic and important pattern of Feedback Loop class: 1. Acquire data from the outer world (Percept) 2. Produce a Project Network, Plan. It may be incomplete, but should allow us to progress (Plan) ◮ Plan is a DAG, actions are nodes, edges are dependencies 3. Execute the Plan (Execute). ◮ Perform the steps of the Plan ◮ Mark your Plan nodes according to the results of their execution ◮ Let’s call marked plan as Trace 4. Go to step 1 unless termination criteria reached (Repeat) Let’s call it PPER (pronounced: pepper). 1 Slides: https://goo.gl/okZ8Bw 15 / 44
  • 16. distage distage: Staged DI for Scala Garbage Collector and its Benefits Garbage Collector and Context Minimization 1. Let’s assume that we have a UsersService and AccountingService in your context, 2. . . . and we want to write a test for UsersService only, 3. We may exploit staged design and collect the garbage out of Plan before executing it. 4. We define a garbage collection root, UsersService, and keep only the operations it transitively depends on. The rest is being thrown out even before it’s being instantiated, 5. Garbage Collector allows us to compose contexts easier. 16 / 44
  • 17. distage distage: Staged DI for Scala Garbage Collector and its Benefits Garbage Collector and Context Minimization 17 / 44
  • 18. distage distage: Staged DI for Scala Garbage Collector and its Benefits Context Minimization for Tests Context minimization allows us to: 1. Instantiate only the instances which are required for your tests, 2. Save on test startup time (savings may be significant), 3. Save on configuring per-test contexts manually (savings may be substantial). 18 / 44
  • 19. distage distage: Staged DI for Scala Garbage Collector and its Benefits Context Minimization for Deployment Context minimization allows us to: 1. Have one image with all our software components (Roles1), 2. . . . keeping development flows of these components isolated, 3. Decide which components we want to run when we start the image, 4. Have higher computational density 5. substantially simplify development flows: we may run full environment with a single command on a low-end machine, 6. Fuse Microservices with Monoliths keeping all their benefits. 1 server1# docker run -ti company/product +analytics 2 server2# docker run -ti company/product +accounting +users 3 laptop# docker run -ti company/product --run-everything 1 Slides: https://goo.gl/iaMt43 19 / 44
  • 20. distage distage: Staged DI for Scala Scala Typesystem Integration: Fusional Programming dist✪ge Scala Typesystem Integration: Fusional Programming 20 / 44
  • 21. distage distage: Staged DI for Scala Scala Typesystem Integration: Fusional Programming Kind-Polymorphic Type Tags ◮ distage uses types as lookup keys ◮ But scalac has TypeTags only for kind "*"! ◮ distage added any-kinded ‘Tag‘s ◮ Modules can be polymorphic on types of any kind 1 def m1[F[_]: TagK: Monad, A: Encoder] 2 = new ModuleDef { 3 make[SendJson[F, A]] 4 addImplicit[Encoder[A]] 5 addImplicit[Monad[F]] 6 make[Network[F]].from(...) } 7 // `Tag` implicit accepts 8 // type parameters of arbitrary kind 9 def mTrans[FT[_[_], _]: Tag.auto.T] = 10 m1[FT[IO], AccountReport] 11 mTrans[OptionT] 1 type TagK[F[_]] = HKTag[{ type Arg[A] = F[A] }] 2 type TagKK[F[_, _]] = HKTag[{ type Arg[A, B] = F[A, B] }] 3 type Tag.auto.T[F[...]] = HKTag[{ type Arg[...] = F[...] }] 21 / 44
  • 22. distage distage: Staged DI for Scala Scala Typesystem Integration: Fusional Programming Typeclass instance injection (Implicit Injection) 1. distage wires all parameters, inlcuding implicits 2. Leaving implicits solely to implicit resolution would’ve been unmodular, because we want to bind instances late, not at definition time 3. Therefore implicits should be declared explicitly 4. Tagless-final style is encouraged – add implicit parameters, instead of importing concrete implicits! 1 def polyModule[F[_]: Sync: TagK] = new ModuleDef { 2 make[Sync[F]].from(implicitly[Sync[F]]) 3 make[F[Unit]].named("hello").from( 4 S: Sync[F] => S.delay(println("Hello World!"))) 5 } 6 Injector() 7 .produce(polyModule[IO]) 8 .get[IO[Unit]]("hello").unsafeRunSync() 22 / 44
  • 23. distage distage: Staged DI for Scala Scala Typesystem Integration: Fusional Programming Code example: Tagless Final Style 1 trait Interaction[F[_]] { 2 def say(msg: String): F[Unit] 3 def ask(prompt: String): F[String] } 4 5 class TaglessHelloWorld[F[_]: Monad: Interaction] { 6 val program = for { 7 userName <- Interaction[F].ask("What's your name?") 8 _ <- Interaction[F].say(s"Hello $userName!") 9 } yield () } 10 11 def wiring[F[_]: TagK: Sync] = new ModuleDef { 12 make[TaglessHelloWorld[F]] 13 make[Interaction[F]].from(new Interaction[F] {...}) 14 addImplicit[Monad[F]] } 15 16 Injector().produce(wiring[IO]).get[TaglessHelloWorld[IO]] 17 .program.unsafeRunSync() 23 / 44
  • 24. distage distage: Staged DI for Scala Scala Typesystem Integration: Fusional Programming Code example: Scalaz ZIO Injection 1 trait MonadFilesystem[F[_, _]] { 2 def readFile(fileName: String): F[Exception, String] 3 } 4 5 class ZioFilesystemModule extends ModuleDef { 6 make[MonadFilesystem[IO]].from(new MonadFilesystem[IO] { 7 def readFile(fileName: String) = 8 IO.syncException(Source.fromFile(fileName)("UTF-8") 9 .mkString) 10 }) 11 make[GetCpus[IO]] 12 } 13 14 class GetCpus(fs: MonadFilesystem[IO]) { 15 def apply(): IO[Exception, Int] = 16 fs.readFile("/proc/cpuinfo").flatMap(...) 17 } 24 / 44
  • 25. distage distage: Staged DI for Scala Scala Typesystem Integration: Fusional Programming Lambda Injection, ProviderMagnet 1. We can bind functions as constructors 2. Arbitrary-arity functions can run with arguments from Locator 3. Put annotations on types to summon named instances or config values (from distage-config) 1 case class A() 2 case class B(a: A, default: Int = 5) 3 val ctx: Locator = Injector(config).produce(new ModuleDef { 4 make[A].from(A()) 5 make[B].from{ a: A => B(a) } 6 }) 7 case class C(a: A, b: B, host: HostPort) 8 val c: C = ctx.run { 9 (a: A, b: B, host: HostPort @ConfPath("http.listen")) => 10 C(a, b, host) 11 } 25 / 44
  • 26. distage distage: Staged DI for Scala Scala Typesystem Integration: Fusional Programming Path-dependent types 1. Path-dependent types and projections are supported 2. Path-prefix should be wired for the child type to be wired 1 trait Cake { trait Child } 2 3 val cakeModule = new ModuleDef { 4 make[Cake] 5 make[Cake#Child] 6 } 7 val cake = Injector().produce(cakeModule).get[Cake] 8 9 val instanceCakeModule = new ModuleDef { 10 make[cake.type].from[cake.type](cake: cake.type) 11 make[cake.Child] 12 } 13 val cakeChild = Injector().produce(instanceCakeModule) 14 .get[cake.Child] 26 / 44
  • 27. distage distage: Staged DI for Scala Features to boost productivity dist✪ge Features to boost productivity 27 / 44
  • 28. distage distage: Staged DI for Scala Features to boost productivity Dynamic Plugins Just drop your modules into your classpath: 1 class AccountingModule extends PluginDef { 2 make[AccountingService].from[AccountingServiceImpl] 3 // ... 4 } Then you may pick up all the modules and build your context: 1 val plugins = new PluginLoaderDefaultImpl( 2 PluginConfig(Seq("com.company.plugins")) 3 ).load() 4 // ... pass to an Injector 1. Useful while you are prototyping your app, 2. In maintenance phase you may switch to static configuration. 28 / 44
  • 29. distage distage: Staged DI for Scala Features to boost productivity Dynamic Testkit (ScalaTest) 1 class AccountingServiceTest extends DistagePluginSpec { 2 "accounting service" must { 3 "be resolved dynamically" in di { 4 (acc: AccountingService) => 5 assert(acc.getBalance("bad-user") == 0) 6 } 7 } 8 } 1. You don’t need to setup your context, it’s done automatically by Plugin Loader and Garbage Collector, 2. And it takes just milliseconds, not like in Spring, 3. Garbage collection roots are inferred from test’s signature, 4. Only the things required for a particular test are being instantiated. 29 / 44
  • 30. distage distage: Staged DI for Scala Features to boost productivity Tags 1. Each binding may be marked with a tag, 2. Some bindings may be excluded from the context before planning by a predicate, 3. This is unsafe but convenient way to reconfigure contexts conditionally. 1 class ProductionPlugin extends PluginDef { 2 tag("production", "cassandra") 3 make[UserRepo].from[CassandraUserRepo] 4 } 5 class MockPlugin extends PluginDef { 6 tag("test", "mock") 7 make[UserRepo].from[MockUserRepo] 8 } 9 // ... 10 val disabledTags = t"mock" && t"dummy" 11 val plan = injector.plan(definition.filter(disabledTags)) 30 / 44
  • 31. distage distage: Staged DI for Scala Features to boost productivity Circular dependencies 1. Supported, Proxy concept used, 2. By-name parameters (class C(param: => P)) supported, no runtime code-generation requried, 3. Other cases are supported as well with runtime code-generation, 4. Limitations: typical. You cannot use an injected parameter immediately in a constructor, you cannot have circular non-by-name dependencies with final classes, 5. Circular dependency resolution is optional, you may turn it off. 31 / 44
  • 32. distage distage: Staged DI for Scala Features to boost productivity Plan Introspection: example context 1 class Cluster 2 trait UsersService 3 trait AccountingService 4 trait UserRepo 5 trait AccountsRepo 6 7 class UserRepoImpl(cluster: Cluster) extends UserRepo 8 class AccountsRepoImpl(cluster: Cluster) extends AccountsRepo 9 class UserServiceImpl(userRepo: UserRepo) extends UsersService 10 class AccountingServiceImpl(accountsRepo: AccountsRepo) 11 extends AccountingService 12 13 class UsersApiImpl(service: UsersService 14 , accountsApi: AccountsApiImpl) 15 class AccountsApiImpl(service: AccountingService 16 , usersApi: UsersApiImpl) // circular dependency 17 class App(uapi: UsersApiImpl, aapi: AccountsApiImpl) 32 / 44
  • 33. distage distage: Staged DI for Scala Features to boost productivity Plan Introspection: example bindings1 1 val definition = new ModuleDef { 2 make[Cluster] 3 make[UserRepo].from[UserRepoImpl] 4 make[AccountsRepo].from[AccountsRepoImpl] 5 make[UsersService].from[UserServiceImpl] 6 make[AccountingService].from[AccountingServiceImpl] 7 make[UsersApiImpl] 8 make[AccountsApiImpl] 9 make[App] 10 } 11 val injector = Injector() 12 val plan = injector.plan(definition) 1 Full code example: https://goo.gl/7ZwHfX 33 / 44
  • 34. distage distage: Staged DI for Scala Features to boost productivity Plan Introspection: plan dumps 1 println(plan.render) // look for the circular dependency! 34 / 44
  • 35. distage distage: Staged DI for Scala Features to boost productivity Plan Introspection: dependency trees You may explore dependencies of a component: 1 val dependencies = plan.topology.dependencies 2 println(dependencies.tree(DIKey.get[AccountsApiImpl])) Circular dependencies are specifically marked. 35 / 44
  • 36. distage distage: Staged DI for Scala Features to boost productivity Trait Completion 1 trait UsersService { 2 protected def repository: UsersRepo 3 def add(user: User): Unit = { 4 repository.put(user.id, user) 5 ??? 6 } 7 } We may bind this trait directly, without an implementation class: 1 make[UsersService] 1. Corresponding class will be generated by distage, 2. Null-arg abstract methods will be wired with context values, 3. Works in both runtime and compile-time. 36 / 44
  • 37. distage distage: Staged DI for Scala Features to boost productivity Factory Methods (Assisted Injection) 1 class UserActor(sessionId: UUID, sessionRepo: SessionRepo) 2 3 trait ActorFactory { 4 def createActor(sessionId: UUID): UserActor 5 } 1. createActor is a factory method, 2. createActor will be generated by distage, 3. non-null-arg abstract methods are treated as factory methods, 4. Non-invasive assisted injection: sessionId: UUID will be taken from method parameter, sessionRepo: SessionRepo will be wired from context, 5. Useful for Akka, lot more convenient than Guice, 6. Works in both runtime and compile-time. 37 / 44
  • 38. distage distage: Staged DI for Scala 7mind Stack dist✪ge 7mind Stack 38 / 44
  • 39. distage distage: Staged DI for Scala 7mind Stack distage: status and things to do distage is: 1. ready to use, 2. in real production, 3. all Runtime APIs are available, 4. Compile-time verification, trait completion, assisted injections and lambda injections are available. Our plans: 1. Refactor Roles API, 2. Support running Producer within a monad (to use with Scalaz ZIO, Monix, cats-effect, etc), 3. Support Scala.js, 4. Support optional isolated classloaders (in foreseeable future), 5. Publish compile-time Producer, 6. Check our GitHub: https://github.com/pshirshov/izumi-r2. 39 / 44
  • 40. distage distage: Staged DI for Scala 7mind Stack distage is just a part of our stack We have a vision backed by our tools: 1. Idealingua: transport and codec agnostic gRPC alternative with rich modeling language, 2. LogStage: zero-cost logging framework, 3. Fusional Programming and Design guidelines. We love both FP and OOP, 4. Continous Delivery guidelines for Role-based process, 5. Percept-Plan-Execute Generative Programming approach, abstract machine and computational model. Addresses Project Planning (see Operations Research). Examples: orchestration, build systems. Altogether these things already allowed us to significantly reduce development costs and delivery time for our client. More slides to follow. 40 / 44
  • 41. distage distage: Staged DI for Scala 7mind Stack You use Guice? Switch to distage! 41 / 44
  • 42. distage distage: Staged DI for Scala 7mind Stack Teaser: LogStage A log call . . . 1 log.info(s"$user logged in with $sessionId!") . . . may be rendered as a text like 17:05:18 UserService.login user=John Doe logged in with sessionId=DEADBEEF! . . . or a structured JSON: 1 { 2 "user": "John Doe", 3 "sessionId": "DEADBEEF", 4 "_template": "$user logged in with $sessionId!", 5 "_location": "UserService.scala:265", 6 "_context": "UserService.login", 7 } 42 / 44
  • 43. distage distage: Staged DI for Scala 7mind Stack Teaser: Idealingua 1 id UserId { uid: str } 2 data User { name: str /* ... more fields */ } 3 data PublicUser { 4 + InternalUser 5 - SecurityAttributes 6 } 7 adt Failure = NotFound | UnknownFailure 8 service Service { 9 def getUser(id: UserId): User !! Failure 10 } 1. Convenient Data and Interface Definition Language, 2. Extensible, transport-agnostic, abstracted from wire format, 3. JSON + HTTP / WebSocket at the moment, 4. C#, go, Scala, TypeScript at the moment, 5. Better than gRPC/ REST / Swagger/ etc. 43 / 44
  • 44. distage distage: Staged DI for Scala 7mind Stack Thank you for your attention distage website: https://izumi.7mind.io/ We’re looking for clients, contributors, adopters and colleagues ;) About the author: 1. coding for 18 years, 10 years of hands-on commercial engineering experience, 2. has been leading a cluster orchestration team in Yandex, “the Russian Google”, 3. implemented “Interstellar Spaceship” – an orchestration solution to manage 50K+ physical machines across 6 datacenters, 4. Owns an Irish R&D company, https://7mind.io, 5. Contact: team@7mind.io, 6. Github: https://github.com/pshirshov 7. Download slides: https://github.com/7mind/slides/ 44 / 44