Обеспечивает разрешение с помощью Scala-cats

Пример взят из Финал без тегов по освоению функционального программирования:

trait Capabilities[F[_]] {
  def resource(name: String): F[String]
  def notify(target: String, text: String): F[Unit]
}

import cats.Monad

def income[F[_]](implicit M: Monad[F], C: Capabilities[F]): F[Unit] =
  for {
    contents <- C.resource("sales.csv")
    total = contents
      .split("\n").toList.tail  // Collection of lines, drop the CSV header
      .map { _.split(",").toList match  // List[Double] - prices of each of the entries
    { case name :: price :: Nil => price.toDouble }
    }
      .sum
    _ <- C.notify("[email protected]", s"Total income made today: $total")
  } yield ()

Чтобы скомпилировать, я должен включить:

import cats.implicits._

Без этого я получаю ошибку:

Ошибка: (21, 27) карта значений не является членом параметра типа F [String] contents ‹- C.resource (" sales.csv ")

Два вопроса:

  1. Тип F заранее не известен. Но есть Monad и Capabilities определяют implicitly для F. Почему компилятор Scala не может идентифицировать его без импортирования из кошачьих.
  2. Обычно я предпочитаю найти определенный тип и не импортирую все из cats. Например, точнее импортировать только cats.instances.list._. Что именно используется компилятором Scala из cats.implicits._, чтобы можно было скомпилировать этот код? И что еще более важно, какой алгоритм вы используете, чтобы его найти?
  3. Я также обнаружил, что если я добавлю параметр -Xprint-args, код будет успешно скомпилирован даже без импорта cats.implicits._. Не могли бы вы объяснить, как это влияет на это?

person Alexandr    schedule 19.08.2019    source источник


Ответы (1)


  1. То, что компилятор Scala не может идентифицировать без импортирования, - это просто синтаксис. Экземпляры разрешаются правильно без импорта (из-за неявных параметров).

  2. Фактически из cats.implicits._ вы используете только

    import cats.syntax.functor._
    import cats.syntax.flatMap._
    
  3. # P3 #
    # P4 #

Без импортированных синтаксисов вы можете написать

def income[F[_]](implicit M: Monad[F], C: Capabilities[F]): F[Unit] =
  M.flatMap(C.resource("sales.csv"))(contents => {
    val total = contents
      .split("\n").toList.tail // Collection of lines, drop the CSV header
      .map {
        _.split(",").toList match // List[Double] - prices of each of the entries
          { case name :: price :: Nil => price.toDouble }
      }
      .sum
    M.map(C.notify("[email protected]", s"Total income made today: $total"))(_ => ())
  })
person Dmytro Mitin    schedule 19.08.2019
comment
благодарю вас! Вопрос в следующем: я думал, что Scala может идентифицировать по сигнатуре, что КОГДА вызывается метод, определенные неявные экземпляры будут в области видимости. А также следует понимать, что если для типа F - монада существует, она должна иметься при вызове функций map / flatMap и других. - person Alexandr; 19.08.2019
comment
@Alexandr Да, но обязательно, когда вызывается map / flatMap и другие функции, это означает, что вы можете вызывать их как Monad[F].flatMap(fa)(f), _2 _... Если вы хотите называть их как fa.flatMap(f), fa.map(f) ..., то дополнительно вам следует импортировать синтаксисы. - person Dmytro Mitin; 19.08.2019
comment
Спасибо, Дмитрий, за разъяснение. Никогда не сомневаюсь в этом в этом контексте. - person Alexandr; 20.08.2019
comment
не могли бы вы показать, как точно вызывать contents <- C.resource("sales.csv") внутри for-complation, когда синтаксис не импортируется. Просто ваи Monad.flatMap функция. - person Alexandr; 20.08.2019
comment
@Alexandr gist.github.com/loicdescotte/4044169 Я обновил свой ответ. - person Dmytro Mitin; 20.08.2019
comment
нет, как пользоваться flatMap / map, без понимания, понятно. C.resource("sales.csv") должен вернуть Монаду. Судя по сигнатуре, есть монада для F. Вопрос в том, как мне реализовать функцию income с for-computing, без импорта синтаксиса. Вообще. Например, библиотека кошек не используется. - person Alexandr; 20.08.2019
comment
@Alexandr Невозможно реализовать функцию income с for-computing без синтаксиса (импортированного или реализованного вручную как методы расширения). - person Dmytro Mitin; 20.08.2019
comment
@ Александр implicit class Ops[F[_], A](fa: F[A]) { def flatMap[B](f: A => F[B])(implicit F: FlatMap[F]): F[B] = F.flatMap(fa)(f); def map[B](f: A => B)(implicit F: Functor[F]): F[B] = F.map(fa)(f) } - person Dmytro Mitin; 20.08.2019
comment
Давайте продолжим это обсуждение в чате. - person Alexandr; 20.08.2019