假设我想出了一个组合器:
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]]
以验证可选值的有效性(如果存在)。