Клейсли Стрелка с писателем в Скале. Почему он не компилируется?
Это продолжение моего предыдущего вопроса. Похоже, я до сих пор не понял. Сейчас я пытаюсь составить функции, которые возвращают монаду Writer.
scala> val f = {x: Int => Writer ("выполняется" + x + ";", x + 1)} f: Int => scalaz.WriterT[scalaz.Id.Id,String,Int] = scala> Kleisli(f) >=> Kleisli(f):16: ошибка: параметры типа для метода не применяются: (f: A => M[B])scalaz.Kleisli[M,A,B] в объекте Kleisli существуют так, что его можно применять к аргументам (Int => scalaz.WriterT[scalaz.Id.Id,String,Int]) --- потому что --- тип выражения аргумента не совместим с формальным типом параметра; найдено: Int => scalaz.WriterT [scalaz.Id.Id, String, Int] обязательно:?A =>?M Kleisli(f) >=> Kleisli(f)
Почему он не компилируется?
1 ответ
Когда компилятор Scala ожидает тип в форме M[B]
и вы даете что-то вроде WriterT[Id, String, Int]
К сожалению, это просто недостаточно умно, чтобы понять, что вы хотите исправить первые два параметра типа и использовать монаду для WriterT[Id, String, _]
,
Есть несколько возможных способов обойти это ограничение. Первый - определить псевдоним типа:
type StringWriter[A] = WriterT[Id, String, A]
Теперь вы можете предоставить явные параметры типа (фактически вы можете сделать это без псевдонима, но тип lambdas сделает строку в два раза длиннее и в десять раз нечитаемой):
scala> Kleisli[StringWriter, Int, Int](f) >=> Kleisli[StringWriter, Int, Int](f)
res0: scalaz.Kleisli[StringWriter,Int,Int] = Kleisli(<function1>)
Scalaz теперь предлагает более приятное решение с помощью "неприменимой уловки" Майлза Сабина:
val ff = Kleisli.kleisliU(f) >=> Kleisli.kleisliU(f)
kleisliU
по сути, просто хорошая версия Kleisli.apply
который использует новый тип класса (по имени Unapply
за кулисами, чтобы направить систему определения типа к правильному способу разделения WriterT[Id, String, Int]
,