{"id":19422533,"url":"https://github.com/scalalandio/pulp","last_synced_at":"2025-04-24T15:32:22.217Z","repository":{"id":57736155,"uuid":"108756822","full_name":"scalalandio/pulp","owner":"scalalandio","description":"Scala library for guiceless dependency injection","archived":true,"fork":false,"pushed_at":"2020-10-30T15:31:47.000Z","size":215,"stargazers_count":31,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-17T11:36:20.625Z","etag":null,"topics":["dependency-injection","implicits","macros","scala"],"latest_commit_sha":null,"homepage":"https://scalalandio.github.io/pulp/","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scalalandio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-29T17:47:19.000Z","updated_at":"2025-02-19T12:45:40.000Z","dependencies_parsed_at":"2022-08-24T01:20:19.936Z","dependency_job_id":null,"html_url":"https://github.com/scalalandio/pulp","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalalandio%2Fpulp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalalandio%2Fpulp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalalandio%2Fpulp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalalandio%2Fpulp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scalalandio","download_url":"https://codeload.github.com/scalalandio/pulp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250654480,"owners_count":21465890,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-injection","implicits","macros","scala"],"created_at":"2024-11-10T13:34:05.864Z","updated_at":"2025-04-24T15:32:21.969Z","avatar_url":"https://github.com/scalalandio.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pulp\n\n[![https://travis-ci.org/scalalandio/pulp](https://api.travis-ci.org/scalalandio/pulp.svg?branch=master)](https://travis-ci.org/scalalandio/pulp)\n[![Maven Central](https://img.shields.io/maven-central/v/io.scalaland/pulp_2.12.svg)](http://search.maven.org/#search%7Cga%7C1%7Cpulp)\n[![Scala.js](https://www.scala-js.org/assets/badges/scalajs-0.6.17.svg)](https://www.scala-js.org)\n[![License](http://img.shields.io/:license-Apache%202-green.svg)](http://www.apache.org/licenses/LICENSE-2.0.txt)\n\nToo much fructose is not good for your health, so you should remove\nGuice from your diet. This small experimental project allows you to have\nyour fruity projects with no Guice.\n\n## Getting started\n\nLibrary is available for Scala 2.11, 2.12, 2.13-M4 and Scala.js 0.6\n(Scala.js without 2.13.0-M4 due to a compiler bug in former ).\n\nAdd it with (2.11, 2.12):\n\n```scala\nlibraryDependencies += \"io.scalaland\" %% \"pulp\" % pulpVersion\naddCompilerPlugin(\"org.scalamacros\" % \"paradise\" % \"2.1.0\" cross CrossVersion.full)\n```\n\nor if you cross-build with Scala.js (2.11, 2.12):\n\n```scala\nlibraryDependencies += \"io.scalaland\" %%% \"pulp\" % pulpVersion\naddCompilerPlugin(\"org.scalamacros\" % \"paradise\" % \"2.1.0\" cross CrossVersion.full)\n```\n\nor with Scala 2.13:\n\n```scala\nlibraryDependencies += \"io.scalaland\" %% \"pulp\" % pulpVersion\nscalacOptions += \"-Ymacro-annotations\"\n```\n\nLatest version can be checked on Maven and is displayed on the badge above.\n\nAmmonite users can try it out with:\n\n```scala\nimport $ivy.`io.scalaland:pulp_2.12:0.0.8`, io.scalaland.pulp._\ninterp.load.plugin.ivy(\"org.scalamacros\" % \"paradise_2.12.4\" % \"2.1.0\")\n```\n\nWith Ammonite 1.1.0 you can [try out this showoff code](https://gist.github.com/MateuszKubuszok/595b1b6cb409f2ef0cbf5d5c914e1e1b)!\n\n## Usage\n\nSee [DOCS](DOCS.md) for specific cases or read further for understanding the general idea.\n\n## Motivation\n\nI wanted to avoid runtime reflection based dependency injection\nin my program while still avoiding the need to pass everything manually.\nExisting ways of doing DI in Scala that I knew of were:\n\n * manual dependency injection\n * usage of runtime reflection like [Guice](https://github.com/google/guice)\n   or one used by [Spring Framework](https://spring.io)\n * semi-manual DI via something like [MacWire](https://github.com/adamw/macwire)\n * usage of Scala's built-in implicits\n\nAll of above have some pros and cons thought it's mostly up to\nprogrammers' taste to decide which trade off they like better (though\nthey would often defend their own choice as the only reasonable).\n\nI wanted to go with implicits, but that generating a bit of a boilerplate:\n\n```scala\nclass A\nclass B (implicit a: A)\nclass C (implicit b: B)\nclass D (implicit b: B, c: C)\n\nimplicit val a: A = new A\nimplicit val b: B = new B\nimplicit val c: C = new C\n```\n\nAdditionally we pollute the scope with tons of manually written\nimplicits, including these passed by constructor.\n\nInstead we could move them to companion objects and wrap in a dedicated\ntype to ensure they won't accidentally mix with other implicits:\n\n```scala\ntrait Provider[T] { def get(): T }\nobject Provider { def get[T: Provider]: T = implicitly[Provider[T]].get() }\n\nclass A\nobject A { implicit def provide: Provider[A] = () =\u003e new A }\nclass B (a: A)\nobject B { implicit def provide(implicit a: Provider[A]): Provider[B] = () =\u003e new B(a.get()) }\nclass C (b: B)\nobject B { implicit def provide(implicit b: Provider[B]): Provider[C] = () =\u003e new C(b.get()) }\nclass D (b: B, c: C)\nobject D { implicit def provide(implicit b: Provider[B], c: Provider[C]): Provider[D] = () =\u003e new D(b.get(), c.get()) }\n\nProvider.get[D]\n```\n\nHowever, as we can see it brings a lot of boilerplate to the table.\nBut what if we generated all of that code? E.g. with macro annotations:\n\n```scala\n@Wired class A\n@Wired class B (a: A)\n@Wired class C (b: B)\n@Wired class D (b: B, c: C)\n\nProvider.get[D]\n```\n\nThat's basically what Pulp does.\n\n## Features\n\n * both monomorphic and polymorphic classes\n * existing companion objects will be extended and missing generated\n * class might have or have not dependencies passed via constructor\n * type-class derivation via `import io.scalaland.pulp.semiauto._`\n\n## Limitations\n\nPulp uses implicits for passing objects around. It means that\n`Provider[T]` must be in scope of initialization for each dependency\nrequired by our class. We might pass it manually, write implicit by hand\nor take from companion object - remember however that only classes\nannotated with `@Wired` will have implicit `Provider`s generated.\n\nAdditionally whether something will have one or more instances is not\nguaranteed for `@Wired` - if one need to ensure that there will be only\none Provider or that each Provider of some type will always return new\ninstance one should use `@Singleton` or `@Factory`. If there might be\narguments available in first usage scopes, Provider needs arguments from\nscope, but `@Singleton` doesn't work you might use `@Cached`.\n\nLast but not least such implementation of `Provider`s is invariant - if\nwe have `trait A` and `@Wired class AImpl extends A` it will not be\nresolved for `A` unless we explicitly provide\n\n```scala\nimplicit val a = Provider.upcast[AImpl, A]\n```\n\nor (if implementation is accessible to interface's scope):\n\n```scala\n@ImplementedAs[AImpl] class A\n\n@Wired class AImpl extends A\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscalalandio%2Fpulp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscalalandio%2Fpulp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscalalandio%2Fpulp/lists"}