trying to implement Kleisli category for a made-up Partial type in Scala (reading Bartosz Milewski's "category theory for programmers", that's exersize for chapter 4)
object Kleisli {
type Partial[A, B] = A => Option[B]
implicit class KleisliOps[A, B](f1: Partial[A, B]) {
def >=>[C](f2: Partial[B, C]): Partial[A, C] =
(x: A) =>
for {
y <- f1(x)
z <- f2(y)
} yield z
def identity(f: Partial[A, B]): Partial[A, B] = x => f(x)
}
val safeRecip: Partial[Double, Double] = {
case 0d => None
case x => Some(1d / x)
}
val safeRoot: Partial[Double, Double] = {
case x if x < 0 => None
case x => Some(Math.sqrt(x))
}
val safeRootRecip: Partial[Double, Double] = safeRoot.>=>(safeRecip)
safeRootRecip(1d)
safeRootRecip(10d)
safeRootRecip(0d)
}
IDE (IntelliJ) shows no errors, but when I run this snippet, I get:
Error:(27, 57) value >=> is not a member of $line5.$read.$iw.$iw.Kleisli.Partial[Double,Double]
val safeRootRecip: Partial[Double, Double] = safeRoot.>=>(safeRecip)
Defining >=> outside of implicit class works fine. What could be the reason?
@sinanspd was right. In Dotty the code seems to compile: https://scastie.scala-lang.org/n17APWgMQkWqy93ct2cghw
Manually resolved
compiles but compiler doesn't find this conversion itself
It seems type parameter
Ais not inferred.(By the way, here Martin Odersky explains why presence of implicit conversions in language makes type inference worse: https://contributors.scala-lang.org/t/can-we-wean-scala-off-implicit-conversions/4388)
Try to make
Partialcovariant with respect toBand (especially) contravariant with respect toA(similarly toA => Option[B]being covariant with respect toBand contravariant with respect toA)Then the code seems to compile.
Another workaround is to replace implicit conversions (
X => Y,KleisliOps) with a type class (MyTransform) and implicit conversion (myConversion) defined in terms of this type class (sometimes this helps with implicit conversions)