当我使用“Reader monad”时如何注入多个依赖项为依赖注入? [英] How to inject multi dependencies when I use "Reader monad" for dependency injection?
问题描述
我正在尝试使用 Reader monad
进行依赖注入,但是当方法需要不同的依赖关系时会出现问题:
I'm trying to use Reader monad
for dependency injection, but have problems when the methods requires different dependencies:
class PageFetcher {
def fetch(url: String) = Reader((dep1: Dep1) => Try {
...
})
}
class ImageExtractor {
def extractImages(html: String) = Reader((deps: (Dep2, Dep3)) => {
...
})
}
object MyImageFinder {
def find(url: String) = Reader((deps: (PageFetcher, ImageExtractor)) => {
val (pageFetcher, imageExtractor) = deps
for {
htmlTry <- pageFetcher.fetch(url)
html <- htmlTry
images <- imageExtractor.extractImages(html)
} yield images
})
}
// I add these 3 useless dependencies here just for demo
class Dep1
class Dep2
class Dep3
您可以看到 PageFetcher.fetch
和 ImageExtractor.extractImages
和 MyImageFinder.find
都有不同的依赖关系。
You can see PageFetcher.fetch
and ImageExtractor.extractImages
and MyImageFinder.find
all have different dependencies.
我不知道我是否正确使用 Reader
的方式,很快当我将它们组合在一起并希望传递依赖项时,我不知道如何要做到这一点:
I'm not sure if the way I use the Reader
correctly, and soon when I combine them together and want to pass the dependencies, I don't know how to do it:
val pageFetcher = new PageFetcher
val imageExtractor = new ImageExtractor
val dep1 = new Dep1
val dep2 = new Dep2
val dep3 = new Dep3
def main(args: Array[String]) {
args.headOption match {
case Some(url) =>
MyImageFinder.find(url)(???) match {
case Success(images) => images.foreach(println)
case Failure(err) => println(err.toString)
}
case _ => println("Please input an url")
}
}
注意代码 MyImageFinder.find(url)(???)
,我想传递依赖关系,如 pageFetcher / imageExtractor / dep1 / dep2 / dep3
,不管我怎么尝试,只是无法编译。
Notice the code MyImageFinder.find(url)(???)
, I want to pass the dependencies like pageFetcher/imageExtractor/dep1/dep2/dep3
, and no matter how I tried, it just can't be compiled.
我的方式是使用 Reader
正确吗?如何轻松地传递依赖项?
Is my way to use Reader
correct? How can I pass the dependencies easily?
推荐答案
如果您想在中使用多个读者
-comprehension,参数类型将需要是相同的方法。一个简单的方法就是将所有内容捆绑在一个环境类型中(它可能只是一个元组),然后使用它作为所有读者的依赖。
If you want to use multiple readers in a for
-comprehension, the argument types will need to be the same, one way or another. One easy way is just to bundle everything up in an environment type (it could just be a tuple), and then use that as the dependency for all your readers.
抛出很多有关这些类型的细粒度依赖关系的信息,但是您也可以使用 local
作为 for
-comprehension:
That throws away a lot of information about fine-grained dependencies in the types, though, and you can also use local
as a kind of map over the input in the for
-comprehension:
case class Foo(i: Int)
case class Bar(s: String)
case class Config(foo: Foo, bar: Bar)
val doSomethingWithFoo: Reader[Foo, String] = Reader(foo => "hello " * foo.i)
val doSomethingWithBar: Reader[Bar, String] = Reader(bar => s"bar is $bar")
val doSomethingWithConfig: Reader[Config, String] = for {
resFoo <- doSomethingWithFoo.local(_.foo)
resBar <- doSomethingWithBar.local(_.bar)
} yield (resFoo, resBar)
正如 map
一个函数 A => B
可以将读者[E,A]
更改为读者[E,B]
, local
with E => F
更改读者[F,A]
至读者[E,A]
这种情况是指读者需要的特定环境,并将其自身进行喂养。
Just as map
with a function A => B
can change a Reader[E, A]
to a Reader[E, B]
, local
with E => F
changes Reader[F, A]
to Reader[E, A]
, in this case taking the specific chunk of the environment the reader needs and feeding it in by itself.
请注意, Kleisli还有许多其他组合器
(更一般的类型 - 阅读器
只是的别名Kleisli [Id,_,_]
),值得一读。
Note that there are lots of other combinators on Kleisli
(a more general type—Reader
is just an alias for Kleisli[Id, _, _]
) that are worth reading up on.
这篇关于当我使用“Reader monad”时如何注入多个依赖项为依赖注入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!