当我使用“Reader monad”时如何注入多个依赖项为依赖注入? [英] How to inject multi dependencies when I use "Reader monad" for dependency injection?

查看:217
本文介绍了当我使用“Reader monad”时如何注入多个依赖项为依赖注入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆