зачем нам нужна свободная монада для интерпретации Action to Future

Я написал один пример использования scalaz.Free, чтобы сопоставить действие с будущим, это выглядит довольно круто. Тем не менее, я пытаюсь понять преимущества этого. Я надеюсь, что смогу получить ответ здесь. Вот мой фрагмент кода

Во-первых, я создаю действие, которое является AST.

trait Action[A]

case class GetNumberAction(x: Int) extends Action[Int]
case class GetStringAction(x: String) extends Action[String]
case class ConvertToIntAction(x: String) extends Action[Int]
case class AddAction(x: Int, y: Int) extends Action[Int]

Затем я создаю класс для сопоставления Action с ASTMonad, используя Scalaz Free и Coyonda.

type Functor[A] = Coyoneda[Action, A]
type ASTMonad[A]= Free[Functor, A]

def toMonad[A](action: Action[A]): ASTMonad[A] = Free.liftFC[Action, A](action) 

object ADTMonad {
    def getNumber(x: Int): ASTMonad[Int] = toMonad(GetNumberAction(x))
    def getString(x: String): ASTMonad[String] = toMonad(GetStringAction(x))
    def converToInt(x: String): ASTMonad[Int] = toMonad(ConvertToIntAction(x))
    def add(x: Int, y: Int): ASTMonad[Int] = toMonad(AddAction(x, y))

}

Наконец, я создаю интерпретатор для интерпретации Action to Future.

object Interpreter extends (Action ~> Future) {
    def apply[A](action: Action[A]): Future[A] = {
        action match {
            case GetNumberAction(x) => Future(x)
            case GetStringAction(x) => Future(x)
            case ConvertToIntAction(x) => Future(x.toInt)
            case AddAction(x, y) => Future(x + y)
        }
    }
}

Когда я запускаю его, я могу использовать

val chain =  for {
    number <- ASTMonad.getNumber(x)
    str <- ASTMonad.getString(y)
    convertedNumber <- ASTMonad.converToInt(str)
    total <- ASTMonad.add(number, convertedNumber)
    } yield total

chain.runWith(Interpreter)

Кажется, это работает, и я думаю, что понимаю эту монаду и интерпретатор. Однако я думаю, каковы преимущества по сравнению с решением, если я использую Future.flatmap и map напрямую?

for {
    number <- Future(x)
    str <- Future(y)
    convertedNumber <- Future(str.toInt)
    total <- Future(number + convertedNumber)
} yield total

Код использования Future flatmap и map мне кажется проще. Итак, вернемся к моим вопросам: нужно ли нам использовать монаду Free для интерпретации бизнес-логики в Future, поскольку Future уже предоставила flatMap и карту. Если это так, может ли кто-нибудь привести мне более конкретный пример, чтобы я мог увидеть преимущества?

заранее спасибо


person Xiaohe Dong    schedule 13.12.2015    source источник


Ответы (1)


Хорошим и мотивированным примером использования бесплатных аппликативов являются синтаксические анализаторы командной строки, назовем тип CLI[A].

Значение типа CLI[A] означает, что вы получите A, если вы предоставите аргументы командной строки (Array[String]) и их можно будет успешно проанализировать. Теперь эта функциональность изоморфна Array[String] -> Either[String,A] при использовании Either для обработки ошибок.

Поскольку вы сделали CLI аппликативным, вы можете map и apply (объединять) значения. Например, вы можете создать Int аргумент count, другой Int аргумент count2 и объединить их в окончательный sum: CLI[Int], содержащий их сумму.

Предположим, вы применяете вычисление напрямую, это дает что-то, что «только» эквивалентно Array[String] -> Either[String,Int]. Но если вы хотите создать текст справки, вы должны знать оба начальных аргумента, и эта информация теряется.

Free спешит на помощь. С помощью Free вы можете сохранить граф вычислений, который вы можете использовать для извлечения всех исходных CLI значений, которые непосредственно анализируются из аргументов. Затем вы можете позже запустить вычисление, которое дает окончательное значение sum, предоставив результаты синтаксического анализа для всех начальных аргументов.

Конечно, вы можете реализовать специальный CLI, который отслеживает все начальные значения во время вычислений, но Free позволит вам избежать этой дополнительной работы.

person ziggystar    schedule 13.12.2015