【发布时间】:2014-10-25 12:27:55
【问题描述】:
我正在编写一个库来通过 API 访问 Web 服务。我已经定义了简单的类来表示 API 操作
case class ApiAction[A](run: Credentials => Either[Error, A])
以及一些执行 Web 服务调用的函数
// Retrieve foo by id
def get(id: Long): ApiAction[Foo] = ???
// List all foo's
def list: ApiAction[Seq[Foo]] = ???
// Create a new foo
def create(name: String): ApiAction[Foo] = ???
// Update foo
def update(updated: Foo): ApiAction[Foo] = ???
// Delete foo
def delete(id: Long): ApiAction[Unit] = ???
我还把 ApiAction 做成了一个单子
implicit val monad = new Monad[ApiAction] { ... }
所以我可以做类似的事情
create("My foo").run(c)
get(42).map(changeFooSomehow).flatMap(update).run(c)
get(42).map(_.id).flatMap(delete).run(c)
现在我在测试它的单子定律时遇到了麻烦
val x = 42
val unitX: ApiAction[Int] = Monad[ApiAction].point(x)
"ApiAction" should "satisfy identity law" in {
Monad[ApiAction].monadLaw.rightIdentity(unitX) should be (true)
}
因为monadLaw.rightIdentity 使用equal
def rightIdentity[A](a: F[A])(implicit FA: Equal[F[A]]): Boolean =
FA.equal(bind(a)(point(_: A)), a)
并且没有Equal[ApiAction]。
[error] could not find implicit value for parameter FA: scalaz.Equal[ApiAction[Int]]
[error] Monad[ApiAction].monadLaw.rightIdentity(unitX) should be (true)
[error] ^
问题是我什至无法想象如何定义Equal[ApiAction]。 ApiAction 本质上是一个函数,我不知道函数上有任何相等关系。当然可以比较运行ApiAction的结果,但是不一样。
我觉得我做的事情非常错误或不理解一些重要的事情。所以我的问题是:
-
ApiAction是一个 monad 有意义吗? -
ApiAction是我设计的吗? - 我应该如何测试它的单子定律?
【问题讨论】:
-
无限域的函数没有可计算的等式关系。您可以使
run成为密封特征的抽象方法,然后将您的ApiActions 实现为派生案例类和案例对象。
标签: scala functional-programming monads equality scalaz