All Projects → scalalandio → pulp

scalalandio / pulp

Licence: Apache-2.0 license
Scala library for guiceless dependency injection

Programming Languages

scala
5932 projects
shell
77523 projects

Projects that are alternatives of or similar to pulp

wedi
[Deprecated] A lightweight dependency injection (DI) library for TypeScript, along with a binding for React.
Stars: ✭ 22 (-15.38%)
Mutual labels:  dependency-injection
sirius-kernel
Provides common core classes and the dependency injection microkernel powering all SIRIUS applications
Stars: ✭ 30 (+15.38%)
Mutual labels:  dependency-injection
ThunderboltIoc
One of the very first IoC frameworks for .Net that has no reflection. An IoC that casts its services before thunder casts its bolts.
Stars: ✭ 40 (+53.85%)
Mutual labels:  dependency-injection
di speed
Speed comparison of Dependency Injection Container
Stars: ✭ 18 (-30.77%)
Mutual labels:  dependency-injection
async-injector
Reactive dependency injection for Rust.
Stars: ✭ 28 (+7.69%)
Mutual labels:  dependency-injection
rodi
Implementation of dependency injection for Python 3
Stars: ✭ 42 (+61.54%)
Mutual labels:  dependency-injection
varie
A Typescript Framework For VueJS
Stars: ✭ 23 (-11.54%)
Mutual labels:  dependency-injection
Slice
Slice - a framework which simplifies Sling/AEM development by using dependency injection pattern and mapping Sling resources into Java objects
Stars: ✭ 64 (+146.15%)
Mutual labels:  dependency-injection
AzureFunctions.Extensions
This provides some useful extensions for Azure Functions.
Stars: ✭ 81 (+211.54%)
Mutual labels:  dependency-injection
vue-ioc
IoC and DI for Vue powered by InversifyJS and inspired by Angular Module syntactic sugar.
Stars: ✭ 39 (+50%)
Mutual labels:  dependency-injection
inject
A simple Kotlin multi-platform abstraction around the javax.inject annotations.
Stars: ✭ 42 (+61.54%)
Mutual labels:  dependency-injection
linker
Dependency Injection and Inversion of Control package
Stars: ✭ 33 (+26.92%)
Mutual labels:  dependency-injection
func-dependency-injection-go
Dependency injection example using higher order functions
Stars: ✭ 26 (+0%)
Mutual labels:  dependency-injection
BESTV
Android TV App powered by TMDb. It is a easy way to find the best TV content, the top movies, series... all of that in your TV.
Stars: ✭ 49 (+88.46%)
Mutual labels:  dependency-injection
movie-booking
An example for booking movie seat, combined of Android Data Binding, State Design Pattern and Multibinding + Autofactory. iOS version is: https://github.com/lizhiquan/MovieBooking
Stars: ✭ 80 (+207.69%)
Mutual labels:  dependency-injection
react-obsidian
Dependency injection framework for React and React Native applications
Stars: ✭ 17 (-34.62%)
Mutual labels:  dependency-injection
redi
💉 A dependency injection library for TypeScript & JavaScript, along with a binding for React.
Stars: ✭ 29 (+11.54%)
Mutual labels:  dependency-injection
Griffin.Container
Inversion of control container with (almost) zero configuration
Stars: ✭ 13 (-50%)
Mutual labels:  dependency-injection
common-injector
Heavily influenced by Angular and it's dependency injection. Inspired by Angular and Indiv.
Stars: ✭ 18 (-30.77%)
Mutual labels:  dependency-injection
graphql-server-typed
Using typescript and gql-code-gen for easy graphql setup
Stars: ✭ 32 (+23.08%)
Mutual labels:  dependency-injection

Pulp

https://travis-ci.org/scalalandio/pulp Maven Central Scala.js License

Too much fructose is not good for your health, so you should remove Guice from your diet. This small experimental project allows you to have your fruity projects with no Guice.

Getting started

Library is available for Scala 2.11, 2.12, 2.13-M4 and Scala.js 0.6 (Scala.js without 2.13.0-M4 due to a compiler bug in former ).

Add it with (2.11, 2.12):

libraryDependencies += "io.scalaland" %% "pulp" % pulpVersion
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)

or if you cross-build with Scala.js (2.11, 2.12):

libraryDependencies += "io.scalaland" %%% "pulp" % pulpVersion
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)

or with Scala 2.13:

libraryDependencies += "io.scalaland" %% "pulp" % pulpVersion
scalacOptions += "-Ymacro-annotations"

Latest version can be checked on Maven and is displayed on the badge above.

Ammonite users can try it out with:

import $ivy.`io.scalaland:pulp_2.12:0.0.8`, io.scalaland.pulp._
interp.load.plugin.ivy("org.scalamacros" % "paradise_2.12.4" % "2.1.0")

With Ammonite 1.1.0 you can try out this showoff code!

Usage

See DOCS for specific cases or read further for understanding the general idea.

Motivation

I wanted to avoid runtime reflection based dependency injection in my program while still avoiding the need to pass everything manually. Existing ways of doing DI in Scala that I knew of were:

  • manual dependency injection
  • usage of runtime reflection like Guice or one used by Spring Framework
  • semi-manual DI via something like MacWire
  • usage of Scala's built-in implicits

All of above have some pros and cons thought it's mostly up to programmers' taste to decide which trade off they like better (though they would often defend their own choice as the only reasonable).

I wanted to go with implicits, but that generating a bit of a boilerplate:

class A
class B (implicit a: A)
class C (implicit b: B)
class D (implicit b: B, c: C)

implicit val a: A = new A
implicit val b: B = new B
implicit val c: C = new C

Additionally we pollute the scope with tons of manually written implicits, including these passed by constructor.

Instead we could move them to companion objects and wrap in a dedicated type to ensure they won't accidentally mix with other implicits:

trait Provider[T] { def get(): T }
object Provider { def get[T: Provider]: T = implicitly[Provider[T]].get() }

class A
object A { implicit def provide: Provider[A] = () => new A }
class B (a: A)
object B { implicit def provide(implicit a: Provider[A]): Provider[B] = () => new B(a.get()) }
class C (b: B)
object B { implicit def provide(implicit b: Provider[B]): Provider[C] = () => new C(b.get()) }
class D (b: B, c: C)
object D { implicit def provide(implicit b: Provider[B], c: Provider[C]): Provider[D] = () => new D(b.get(), c.get()) }

Provider.get[D]

However, as we can see it brings a lot of boilerplate to the table. But what if we generated all of that code? E.g. with macro annotations:

@Wired class A
@Wired class B (a: A)
@Wired class C (b: B)
@Wired class D (b: B, c: C)

Provider.get[D]

That's basically what Pulp does.

Features

  • both monomorphic and polymorphic classes
  • existing companion objects will be extended and missing generated
  • class might have or have not dependencies passed via constructor
  • type-class derivation via import io.scalaland.pulp.semiauto._

Limitations

Pulp uses implicits for passing objects around. It means that Provider[T] must be in scope of initialization for each dependency required by our class. We might pass it manually, write implicit by hand or take from companion object - remember however that only classes annotated with @Wired will have implicit Providers generated.

Additionally whether something will have one or more instances is not guaranteed for @Wired - if one need to ensure that there will be only one Provider or that each Provider of some type will always return new instance one should use @Singleton or @Factory. If there might be arguments available in first usage scopes, Provider needs arguments from scope, but @Singleton doesn't work you might use @Cached.

Last but not least such implementation of Providers is invariant - if we have trait A and @Wired class AImpl extends A it will not be resolved for A unless we explicitly provide

implicit val a = Provider.upcast[AImpl, A]

or (if implementation is accessible to interface's scope):

@ImplementedAs[AImpl] class A

@Wired class AImpl extends A
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].