20

我正在学习 Scala 中的 Free monad,并且我整理了一个简单的代数示例,我可以使用猫将其提升为 Free monad。

这是我的代数

sealed trait ConsultationOp[A]
object consultation {
  case class Create(c: Consultation) extends ConsultationOp[Unit]
  case class Get(s: ConsultationId) extends ConsultationOp[Option[Consultation]]
}

我可以像这样使用它

def app = for {
  c <- consultation.Create(Consultation("123", "A consultation"))
  _ <- consultation.Get(c._id)
} yield ()

def interpreters = ConsultationInterpreter or UserInterpreter
app.foldMap(interpreters)

ConsultationOpto的提升Free是隐式执行的。

(缺少很多细节,完整的工作实现在这里:https ://github.com/gabro/free-api )

到目前为止一切顺利,但是如果我需要提取consultation.Get.

首先想到的是一个单子变换器,即类似

def app = for {
  c <- consultation.Create(Consultation("123", "A consultation")).liftM[OptionT]
  d <- OptionT(consultation.Get(c._id))
  _ <- doSomethingAConsultation(d)
} yield ()

但它看起来很丑,而且感觉不对。

使用 Free monad 时堆叠 monadic 效果的美化方式(如果有的话)是什么?

4

1 回答 1

3

我看到在这些情况下重复出现的常见方法是使用traverse,因此您可以按照以下方式更改代码:

import cats.syntax.traverse._
import cats.instances.option._

// ...

def app = for {
  c <- consultation.Create(Consultation("123", "A consultation"))
  d <- consultation.Get(c._id)
  _ <- d.traverseU(doSomethingAConsultation(_))
} yield ()

恕我直言,这比单子变压器替代品要干净得多。请注意,您可能需要一些其他import并稍微修改代码,我没有尝试过,但概念是:使用遍历。

于 2017-02-09T08:24:03.253 回答