Подтвердить что ты не робот

Используются ли монадные трансформаторы для получения услуг JSON?

У меня есть игра! 2 для приложения Scala, которому необходимо получить некоторые данные в формате JSON из внешней службы.

Игра! framework позволяет асинхронно делать HTTP-запросы, обертывая ответ в Promise. Promise - это монада, которая обертывает значение, которое будет доступно в будущем.

Это хорошо, но в моем случае то, что я получаю от веб-службы, это строка JSON. Мне нужно разобрать его, и синтаксический анализ может завершиться неудачей. Поэтому я должен обернуть все, что попадаю в Option. В результате многие мои методы возвращают Promise[Option[Whatever]]. То есть значение типа Whatever, которое, возможно, будет доступно позже.

Теперь, когда мне приходится работать над таким значением, мне нужно map его дважды. Я думал об этом, следующим образом:

  • создаем новый тип, скажем Hope[A], который обертывает Promise[Option[A]]
  • определение соответствующих методов, таких как map (или, может быть, я должен использовать foreach и наследовать от некоторого символа коллекции?) и flatten
  • предоставлять неявный конвертер между Promise[Option[A]] и Hope[A].

Легко определить map - композиция двух функторов снова является функтором - и flatten может быть сделано явно в этом случае или при составлении монады с Option.

Но мое ограниченное понимание, что мне не нужно изобретать этот материал: трансформатор монады существует именно для этого случая. Или, ну, так я думаю - я никогда не использовал монад-трансформер - и в этом суть вопроса:

Могут ли трансиверы монады использоваться в этой ситуации? Как я могу их фактически использовать?

4b9b3361

Ответ 1

Используя библиотеку Scalaz library OptionT, вы должны иметь возможность превращать значения типа Promise[Option[A]] в значения типа OptionT[Promise, A].

Использование Scalaz 7:

import scalaz.OptionT._
val x: OptionT[Promise, Int] = optionT(Promise.pure(Some(123)))

Чтобы использовать это значение, например, для вызова map или flatMap на нем, вам нужно будет предоставить соответствующий тип для Promise (Functor для map, Monad для flatMap).

Так как Promise является монадическим, должно быть возможно предоставить экземпляр Monad[Promise]. (Вы получите Functor и Applicative бесплатно, потому что классы типов образуют иерархию наследования.) Например (примечание: я не тестировал это!):

implicit val promiseMonad = new Monad[Promise] {
  def point[A](a: => A): Promise[A] = Promise.pure(a)
  def bind[A, B](fa: Promise[A])(f: A => Promise[B]): Promise[B] = fa flatMap f
}

В качестве простого примера теперь вы можете использовать map в OptionT[Promise, A], чтобы применить функцию типа A => B к значению внутри:

def foo[A, B](x: OptionT[Promise, A], f: A => B): OptionT[Promise, B] = x map f

Чтобы получить базовое значение Promise[Option[A]] из OptionT[Promise, A], вызовите метод run.

def bar[A, B](x: Promise[Option[A]], f: A => B): Promise[Option[B]] =
  optionT(x).map(f).run

Вы получите большую выгоду от использования монадных трансформаторов, когда сможете составить несколько операций совместимых типов, сохраняя тип OptionT[Promise, _] между операциями и извлекая базовое значение в конце.

Чтобы составить операции для понимания, вам понадобятся функции типа A => OptionT[Promise, B].

Ответ 2

- удален -

изменить:

Хорошо, вы можете просто использовать scalaz.OptionT здесь:

val x = optionT(Promise { /* api call */ some("""{ "foo": "bar" }""") })
val mapped = x.map(Json.parse).run // run return the resulting Promise[Option[T]]