【问题标题】:Is it possible to optimise a Free Monad program before execution?是否可以在执行前优化 Free Monad 程序?
【发布时间】:2020-05-12 09:16:58
【问题描述】:

我最近使用cats 选择了Free Monad 模式,试图创建一个可以在执行前“简化”的DSL。例如,假设我创建了一种与列表交互的语言:

  sealed trait ListAction[A]
  case class ListFilter[A](in: List[A], p: A => Boolean) extends ListAction[List[A]]
  case class ListMap[A, B](in: List[A], f: A => B) extends ListAction[List[B]]

  type ListProgram[A] = Free[ListAction, A]

在执行使用这些操作构建的任何程序之前,我想通过将后续过滤器转换为单个过滤器并将后续映射转换为单个映射来优化它,以避免多次迭代列表:

// Pseudo code - doesn't compile, just illustrates my intent

def optimise[A](program: ListProgram[A]): ListProgram[A] = {
  case ListFilter(ListFilter(in, p1), p2) => optimise(ListFilter(in, { a: A => p1(a) && p2(a) }))
  case ListMap(ListMap(in, f1), f2) => optimise(ListMap(in, f2 compose f1))
}

这是否可以使用 Free Monad,通过在添加到程序时检查最后一个操作或通过如上所述的优化?非常感谢。


以下是我用来创建程序的代码:

  trait ListProgramSyntax[A] {
    def program: ListProgram[List[A]]

    def listFilter(p: A => Boolean): ListProgram[List[A]] = {
      program.flatMap { list: List[A] =>
        Free.liftF[ListAction, List[A]](ListFilter(list, p))
      }
    }

    def listMap[B](f: A => B): ListProgram[List[B]] = program.flatMap { list =>
      Free.liftF(ListMap(list, f))
    }
  }

  implicit def syntaxFromList[A](list: List[A]): ListProgramSyntax[A] = {
    new ListProgramSyntax[A] {
      override def program: ListProgram[List[A]] = Free.pure(list)
    }
  }

  implicit def syntaxFromProgram[A](existingProgram: ListProgram[List[A]]): ListProgramSyntax[A] = {
    new ListProgramSyntax[A] {
      override def program: ListProgram[List[A]] = existingProgram
    }
  }

例如:

  val program = (1 to 5).toList
    .listMap(_ + 1)
    .listMap(_ + 1)
    .listFilter(_ % 3 == 0)


编辑:在我的同事使用美式拼写搜索“Free Monad optimize”后,我们发现a good answer 对这个问题断言不可能在之前 解释这个问题。

但是,肯定可以解释程序以生成它的优化版本,然后解释它以检索我们的List[A]

【问题讨论】:

  • 肯定可以解释程序以生成它的优化版本” - “执行前”如何?关键是你不能在不调用函数的情况下解释flatMap
  • 是的,我现在看到了。我将探索其他工具来实现我想要的。感谢您的帮助。

标签: scala scala-cats free-monad


【解决方案1】:

我已经设法通过在递归 ADT 中定义我的“程序”结构来获得我想要的:

  sealed trait ListAction[A]
  case class ListPure[A](list: List[A]) extends ListAction[A]
  case class ListFilter[A](previous: ListAction[A], p: A => Boolean) extends ListAction[A]
  case class ListMap[A, B](previous: ListAction[A], f: A => B) extends ListAction[B]

  trait ListActionSyntax[A] {
    def previousAction: ListAction[A]

    def listFilter(p: A => Boolean): ListFilter[A] = ListFilter(previousAction, p)

    def listMap[B](f: A => B): ListMap[A, B] = ListMap(previousAction, f)
  }

  implicit def syntaxFromList[A](list: List[A]): ListActionSyntax[A] = {
    new ListActionSyntax[A] {
      override def previousAction: ListAction[A] = ListPure(list)
    }
  }

  implicit def syntaxFromProgram[A](existingProgram: ListAction[A]): ListActionSyntax[A] = {
    new ListActionSyntax[A] {
      override def previousAction: ListAction[A] = existingProgram
    }
  }

  def optimiseListAction[A](action: ListAction[A]): ListAction[A] = {
    def trampolinedOptimise[A](action: ListAction[A]): Eval[ListAction[A]] = {
      action match {

        case ListFilter(ListFilter(previous, p1), p2) =>
          Eval.later {
            ListFilter(previous, { e: A => p1(e) && p2(e) })
          }.flatMap(trampolinedOptimise(_))

        case ListMap(ListMap(previous, f1), f2) =>
          Eval.later {
              ListMap(previous, f2 compose f1)
          }.flatMap(trampolinedOptimise(_))

        case ListFilter(previous, p) =>
          Eval.defer(trampolinedOptimise(previous)).map { optimisedPrevious =>
            ListFilter(optimisedPrevious, p)
          }

        case ListMap(previous, f) =>
          Eval.defer(trampolinedOptimise(previous)).map { optimisedPrevious =>
            ListMap(optimisedPrevious, f)
          }

        case pure: ListPure[A] => Eval.now(pure)
      }
    }

    trampolinedOptimise(action).value
  }

  def executeListAction[A](action: ListAction[A]): List[A] = {
    def trampolinedExecute[A](action: ListAction[A]): Eval[List[A]] = {
      action match {
        case ListPure(list) =>
          Eval.now(list)

        case ListMap(previous, f) =>
          Eval.defer(trampolinedExecute(previous)).map { list =>
            list.map(f)
          }

        case ListFilter(previous, p) =>
          Eval.defer(trampolinedExecute(previous)).map { list =>
            list.filter(p)
          }
      }
    }

    trampolinedExecute(action).value
  }

这有一个缺点,即我不能免费获得堆栈安全性,并且必须确保我的优化和执行方法得到适当的蹦床。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-31
    • 2010-12-26
    • 2010-11-02
    • 2012-11-27
    • 1970-01-01
    • 2023-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多