Scala не может определить тип параметра в реализации монады Reader - PullRequest
1 голос
/ 07 февраля 2020

Я использую Scala 2.13 и разрабатываю собственную монаду Reader. Реализация монады заключается в следующем.

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)
}

Используя такую ​​монаду, я определяю хранилище для акций.

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

Stocks Сервис использует реализацию хранилища. используя монаду Reader для внедрения зависимости репо.

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

Мой вопрос: почему я должен явно указывать тип параметра repo в последнем определении функции, (repo: StockRepository) => repo.findAll()? Почему компилятор Scala не может выводить тип неявно для меня?

Большое спасибо.

1 Ответ

2 голосов
/ 08 февраля 2020

Конечно, вам нужно только удалить неявное преобразование и добавить apply.
Однако я добавил некоторые другие изменения, чтобы сделать код более идиоматическим c и более простым в использовании, если у вас есть какие-то сомнения, просто дайте мне знать.

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
  }
}
...