【问题标题】:Scala cannot infer parameter type in Reader monad implementationScala 无法在 Reader monad 实现中推断参数类型
【发布时间】:2020-05-24 00:34:22
【问题描述】:

我正在使用 Scala 2.13,并且我正在开发自己的 Reader monad。 monad的实现如下。

object ReaderMonad {

  implicit def reader[From, To](f: From => To): Reader[From, To] = Reader(f)

  case class Reader[From, To](f: From => To) {
    def run(input: From): To =
      f(input)

    def map[NewTo](transformation: To => NewTo): Reader[From, NewTo] =
      Reader(c => transformation(f(c)))

    def flatMap[NewTo](transformation: To => Reader[From, NewTo]): Reader[From, NewTo] =
      Reader(c => transformation(f(c)).run(c))
  }

  def pure[From, To](a: To): Reader[From, To] = Reader((c: From) => a)
}

使用这样的 monad,我正在为股票定义一个存储库。

trait StockRepository {
    def findAll: Map[String, Double]
}

Stocks服务使用repository的实现,使用Reader monad注入repo依赖。

object Stocks {
  def findAll: Reader[StockRepository, Map[String, Double]] = 
    (repo: StockRepository) => repo.findAll()
}

我的问题是,为什么我要在最后一个函数定义(repo: StockRepository) => repo.findAll() 中明确指定repo 参数类型?为什么 Scala 编译器不能为我隐式推断类型?

非常感谢。

【问题讨论】:

  • 我不会在隐式转换中中继,而是使用显式apply。查看 cats 之一。
  • 谢谢,但您的评论没有回答我的问题:P
  • 好的,编译器无法推断输入的类型。它只是看到一个与预期返回类型不匹配的函数,因此它没有任何方法可以推断。对您来说很明显,因为您希望应用隐式转换。但是编译器不能开始假设这么多东西。这就是隐式转换是一种不好的做法的原因之一,如果你希望它发生,那么它会更好。所以我建议使用apply 方法会有所帮助,因为这样编译器就可以推断输入类型。
  • 抱歉,我没看懂你的第一条评论 :( 但是我还是不明白怎么给对象 Reader 添加一个 apply 方法可以解决我的问题。你为什么不发布一个回答这样我可以接受吗?也许,你可以添加一个例子:)

标签: scala type-inference implicit reader-monad


【解决方案1】:

当然,您只需要删除隐式转换并添加apply
但是,我添加了一些其他更改以使代码更惯用且更易于使用,如果您有任何疑问,请告诉我。

object ReaderMonad {
  final case class Reader[From, To] private[ReaderMonad] (run: From => To) {
    def map[NewTo](transformation: To => NewTo): Reader[From, NewTo] =
      Reader(c => transformation(run(c)))

    def flatMap[NewTo](transformation: To => Reader[From, NewTo]): Reader[From, NewTo] =
      Reader(c => transformation(run(c)).run(c))
  }

  // This is all you really need.
  def apply[From, To](f: From => To): Reader[From, To] =
    Reader(run = f)

  // This is called the parially applied trick:
  // https://typelevel.org/cats/guidelines.html
  // It makes easier to use pure.
  private[ReaderMonad] final class PurePartiallyApplied[From](private val dummy: Boolean) extends AnyVal {
    @inline
    final def apply[To](a: To): Reader[From, To] =
      Reader(_ => a)
  }

  // So now, you can just:
  // ReaderMonad.pure[String](10)
  // Instead of:
  // ReaderMonad.pure[String, Int](10)
  def pure[From]: PurePartiallyApplied[From] =
    new PurePartiallyApplied(dummy = true)
}

你可以这样使用它:

trait StockRepository {
  def findAll: Map[String, Double]
}

object Stocks {
  import ReaderMonad.Reader

  def findAll: Reader[StockRepository, Map[String, Double]] =
   ReaderMonad { repo =>
     repo.findAll
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 2011-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多