Let's say I come up with a combinator:
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])
}
This combinator maps any Kleisli[M, A, B]
to Kleisli[M, Option[A], Option[B]
.
However, after some time, I realize (admittedly with the help of estewei on #scalaz) this can be made to work with containers more general than just Option
, namely anything for which there is a Traverse
instance:
def traverseKleisli[M[_]: Applicative, F[_]: Traverse, A, B](k: Kleisli[M, A, B]) =
Kleisli[M, F[A], F[B]](k.traverse)
so that optional
can now be defined as:
def optional[M[_]: Applicative, A, B](fn: Kleisli[M, A, B]) =
traverseKleisli[M, Option, A, B](fn)
However, I'd like to verify that at least the resulting type signature is equal to the original definition of optional
, and whereas I could resort to hovering over both definitions in my IDE (Ensime in my case) and compare the response, I'd like a more solid way of determining that.
I tried:
implicitly[optional1.type =:= optional2.type]
but (obviously?) that fails due to both identifies being considered unstable by scalac
.
Other than perhaps temporarily making both of the functions object
s with an apply
method, are there any easy ways to compare their static types without resorting to relying on hints from IDE presentation compilers?
P.S. the name optional
comes from the fact that I use that combinator as part of a validation DSL to take a Kleisli
-wrapped Validation[String, T]
and turn it into a Kleisli
-wrapped Validation[String, Option[T]]
that verifies the validity of optional values if present.