Scala-cats: композиция для чтения

#scala #scala-cats #for-comprehension #reader-monad

#scala #scala-cats #для-понимания #читатель-монада #считыватель-монада

Вопрос:

 import cats.data.ReaderT
import cats.instances.either._

trait Service1
trait Service2
case class Cats(name:String)

type FailFast[A] = Either[List[String], A]
type Env = (Service1, Service2, Cats)
type ReaderEnvFF[A] = ReaderT[FailFast, Env, A]

def toReaderEnvFF[A](input:A):ReaderEnvFF[A] =
  ReaderT((_:Env) => Right(input))

def c:ReaderEnvFF[Cats] =
  for {
    cats <- toReaderEnvFF((_:Env)._3)
  } yield cats   // This line is 26
  

Ошибка:

Ошибка: (26, 11) несоответствие типов; найдено: T1.this.Env => com.savdev.Cats (который расширяется до) ((com.savdev.Service1, com.savdev.Service2, com.savdev.Cats)) => com.savdev.Требуются кошки: com.savdev.Кошки } дают кошек

Не могли бы вы объяснить, почему cats не является com.savdev.Cats ? И почему в ошибке говорится, что она расширена до функции с методом возврата [Cats] , бот не FailFast[Cats]

Я пытаюсь применить точно такую же логику, как здесь:

 trait Service1 { def s1f = Option(10) }
trait Service2 {
  type ReaderS1[A] = ReaderT[Option,Service1,A]
  import cats.syntax.applicative._
  import cats.instances.option._
  def s2f:ReaderS1[Int] =
    for {
      r2 <- ReaderT((_: Service1).s1f)
      r1 <- 1.pure[ReaderS1]
    } yield r1   r2
}
  

В этом примере я мог бы преобразовать функцию Service1.s1f в ее результат r2, и он отлично работает. Почему я не могу, например, написать что-то вроде:

 for {
 cats <- ReaderT((_:Env)._3)
...
  

Ответ №1:

toReaderEnvFF((_: Env)._3) на самом деле cats <- toReaderEnvFF((_: Env)._3) это toReaderEnvFF[A]((_: Env)._3) для некоторого типа A . Что A сейчас? Поскольку (_: Env)._3 (он же input в toReaderEnvFF ) имеет тип, Env => Cats тогда тип A является Env => Cats . So toReaderEnvFF((_: Env)._3) имеет тип ReaderEnvFF[Env => Cats] , а cats in cats <- toReaderEnvFF((_: Env)._3) имеет тип Env => Cats .

x <- SomeMonad[T] Переменная x In имеет тип T (теперь SomeMonad есть ReaderEnvFF , T is Env => Cats ).

ReaderT((_: Service1).s1f) в вашем втором примере имеет тип, ReaderT[Option, Service1, Int] поэтому r2 в r2 <- ReaderT((_: Service1).s1f) имеет тип Int . Но в вашем первом примере toReaderEnvFF((_: Env)._3) имеет тип ReaderEnvFF[Env => Cats] , иначе ReaderT[FailFast, Env, Env => Cats] поэтому cats в cats <- toReaderEnvFF((_: Env)._3) имеет тип Env => Cats . В этом разница.

Если вы хотите работать с ReaderEnvFF[Cats] , вам следует измениться cats <- toReaderEnvFF(???) . Например

 def c:ReaderEnvFF[Cats] =
  for {
    cats <- toReaderEnvFF(Cats("aaa"))
  } yield cats 
  

Комментарии:

1. но могу ли я получить cats из depedency? Потому что Cats является частью Env

2. @Alexandr Откуда взялся? Если у вас есть ReaderEnvFF[Env] (например, с Kleisli#tap ), вы можете получить ReaderEnvFF[Cats] Kleisli#map(..) ) .

3. вы знаете, это все еще не ясно. В r2 <- ReaderT((_: Service1).s1f) типе r2 это Int, но в том же / подобном выражении: cats <- ReaderT((_:Env)._3) cats имеет тип Env => Cats . Для меня синтаксис тот же. Я все еще не вижу разницы. Атрибуты ReaderT не могут повлиять на это правильно? Не могли бы вы объяснить это другими словами.

4. @Alexandr (_: Service1).s1f имеет тип Service1 => Option[Int] , поэтому ReaderT((_: Service1).s1f) имеет тип ReaderT[Option,Service1,Int] , поэтому r2 in r2 <- ReaderT((_: Service1).s1f) имеет тип Int . (_:Env)._3 имеет тип Env => Cats , поэтому ReaderT((_:Env)._3) имеет тип ReaderT[Id,Env,Cats] , поэтому cats in cats <- ReaderT((_:Env)._3) имеет тип Cats .

5. @Alexandr Дело не в синтаксисе, а в типах.