Читательская монада в 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 типа, потому что иногда у скалы есть проблемы с выбором правильного. И лично я не нахожу это грязным, чтобы вставить некоторые типы самостоятельно.

Возможно, стоит заглянуть в кошек, хотя их пока немного.

Другие вопросы по тегам