Читательская монада в Scala: возврат, локальный и последовательность
Я использую Reader
монада в Scala, предоставленная библиотекой scalaz. Я знаком с этой монадой, как определено в Haskell. Проблема в том, что я не могу найти функции, эквивалентные return
, local
, а также sequence
(среди прочих).
В настоящее время я использую конструкции, которые мне не нравятся, поскольку я повторяю себя или делаю свой код немного неясным.
относительно return
Я сейчас использую:
Reader{_ => someValue}
Я бы предпочел просто использовать такую конструкцию, как unit(someValue)
, но я не смог ничего найти в интернете. Есть учебники, подобные этому, которые используют описанный выше подход, и который я считаю неоптимальным.
относительно local
Я также должен сделать что-то подобное: вместо того, чтобы печатать что-то вроде: local f myReader
Я должен раскрыть свое определение:
Reader{env => myReader.run(f(env))
Наконец, последовательность немного ближе к тому, что я ожидал (будучи беженцем из Хаскелла в Скале):
readers: List[Reader[Env, T]]
readerTs: Reader[Env, List[T]] = readers.sequenceU
Моя проблема с этой реализацией заключается в том, что, будучи относительно новым для Scala, тип sequenceU
final class TraverseOps[F[_],A] private[syntax](val self: F[A])(implicit val F: Traverse[F]) extends Ops[F[A]] {
//...
def sequenceU(implicit G: Unapply[Applicative, A]): G.M[F[G.A]]
кажется довольно туманным и похоже на черную магию. В идеале я хотел бы использовать sequence
операции на монадах.
Есть ли лучший перевод этих конструкций в Scala, доступный в scalaz или аналогичной библиотеке? Я не женат ни на одной функциональной библиотеке для Scala, поэтому подойдет любое решение, использующее другие библиотеки, хотя я бы предпочел получить ответ с использованием scalaz, так как я уже реализовал свой код, используя его.
1 ответ
Чтобы упростить ситуацию, я добавлю несколько типов. Изменение их в def с универсальными типами должно работать. Также я извлек ReaderInt
типа, чтобы не путать с лямбдами типа.
возврат / чистый / точка
Scala не имеет автоматического разрешения классов типов, поэтому вам нужно предоставить их неявно. За Kleisli
(будучи монадой-трансформером для читателя), Kleisli[Id, ?, ?]
достаточно
implicit val KA = scalaz.Kleisli.kleisliIdApplicative[Int]
type ReaderInt[A] = Kleisli[Id.Id, Int, A]
val alwaysHello = KA.point("hello")
или с импортированным синтаксисом:
import scalaz.syntax.applicative._
val alwaysHello = "hello".point[ReaderInt]
Так что, как правило, вы
1) импортировать аппликативный экземпляр, который обычно находится в scalaz.std.something.somethingInstance
2) import scalaz.syntax.something._
3) тогда можно написать x.point[F]
, где F
ваш аппликативный.
местный
Не уверен, что он отвечает на ваш вопрос, но Kleisli
имеет local
метод.
val f: String ⇒ Int = _.length
val alwaysEleven = alwaysHello local f
последовательность действий
Точно так же вы можете свободно использовать syntax
для или указать классы типов явно.
import scalaz.std.list.listInstance
val initial: List[ReaderInt[String]] = ???
val sequenced: ReaderInt[List[String]] = Traverse[List].sequence[ReaderInt, String](initial)
import scalaz.syntax.traverse._
val z = x.sequence[ReaderInt, String]
Я предпочитаю не использовать sequenceU
, который использует Unapply
typelcass сделать вывод G
типа, потому что иногда у скалы есть проблемы с выбором правильного. И лично я не нахожу это грязным, чтобы вставить некоторые типы самостоятельно.
Возможно, стоит заглянуть в кошек, хотя их пока немного.