0

假设我想出了一个组合器:

def optional[M[_]: Applicative, A, B](fn: Kleisli[M, A, B]) =
  Kleisli[M, Option[A], Option[B]] {
    case Some(t) => fn(t).map(_.some)
    case None => Applicative[M].point(none[B])
  }

此组合器将 any 映射Kleisli[M, A, B]Kleisli[M, Option[A], Option[B].

然而,一段时间后,我意识到(诚然,在 #scalaz 上的 estewei 的帮助下)这可以与更通用的容器一起工作,而不仅仅是Option,即任何有Traverse实例的容器:

def traverseKleisli[M[_]: Applicative, F[_]: Traverse, A, B](k: Kleisli[M, A, B]) =
  Kleisli[M, F[A], F[B]](k.traverse)

所以optional现在可以定义为:

def optional[M[_]: Applicative, A, B](fn: Kleisli[M, A, B]) =
  traverseKleisli[M, Option, A, B](fn)

但是,我想验证至少生成的类型签名是否等于 的原始定义optional,而我可以将鼠标悬停在我的 IDE 中的两个定义上(在我的例子中是 Ensime)并比较响应,我会就像确定它的更可靠的方法。

我试过了:

implicitly[optional1.type =:= optional2.type]

但是(显然?)由于两个标识都被认为不稳定而失败scalac

除了可能暂时object用一个apply方法制作这两个函数之外,还有什么简单的方法可以比较它们的静态类型而不依赖于 IDE 表示编译器的提示?

PS 这个名字optional来源于我使用该组合器作为验证 DSL的一部分来获取Kleisli-wrappedValidation[String, T]并将其转换为Kleisli-wrappedValidation[String, Option[T]]以验证可选值的有效性(如果存在)。

4

1 回答 1

1

您遇到的问题是方法不是 scala 中的值,并且值是单一类型的。您可以测试您的方法的特定“实例”是否具有正确的类型(使用 shapeless 中的实用函数):

val optional1Fix = optional1[Future, Int, String] _
val optional2Fix = optional2[Future, Int, String] _
import shapeless.test._
sameTyped(optional1Fix)(optional2Fix)

但是与单元测试一样,这有点令人不满意,因为即使我们测试了几个实例,我们也不能确定它适用于所有情况。(请注意implicitly[optional1Fix.type =:= optional2Fix.type],我认为这仍然不起作用,因为编译器永远不会意识到两个路径相关类型何时相等)。

对于完整的解决方案,我们必须将完整的函数视为一个值,因此我们必须将其替换为您建议objectapply方法(类似于 shapeless' ~>)。我能想到的唯一替代方法是宏,它必须应用于包含方法的对象,并且知道要比较哪些方法;为这个特定的测试编写一个特定的宏似乎有点过分。

于 2014-10-24T19:14:07.040 回答