Scala Kleisli 在 IntelliJ 中抛出错误
Scala Kleisli throws an error in IntelliJ
尝试在 Scala 中为虚构的部分类型实现 Kleisli 类别(阅读 Bartosz Milewski 的“程序员的类别理论”,这是第 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) 没有显示任何错误,但是当我 运行 这个片段时,我得到:
Error:(27, 57) value >=> is not a member of $line5.$read.$iw.$iw.Kleisli.Partial[Double,Double]
val safeRootRecip: Partial[Double, Double] = safeRoot.>=>(safeRecip)
在隐式 class 之外定义 >=>
工作正常。可能是什么原因?
@sinanspd 是对的。在 Dotty 中,代码似乎可以编译:
https://scastie.scala-lang.org/n17APWgMQkWqy93ct2cghw
手动解决
val safeRootRecip: Partial[Double, Double] = KleisliOps(safeRoot).>=>(safeRecip)
编译但编译器本身找不到此转换
Information: KleisliOps{<null>} is not a valid implicit value
for App.safeRoot.type => ?{def >=> : ?} because:
type mismatch;
found : App.safeRoot.type (with underlying type App.Partial[Double,Double])
required: App.Partial[A,Double]
(which expands to) A => Option[Double]
val safeRootRecip: Partial[Double, Double] = safeRoot.>=>(safeRecip)
似乎没有推断类型参数 A
。
(顺便说一下,Martin Odersky 在这里解释了为什么语言中存在隐式转换会使类型推断变得更糟:https://contributors.scala-lang.org/t/can-we-wean-scala-off-implicit-conversions/4388)
尝试使 Partial
与 B
协变,并且(特别是)与 A
协变(类似于 A => Option[B]
与 [= 协变) 16=] 和关于 A
)
的逆变
type Partial[-A, +B] = A => Option[B]
然后代码似乎可以编译了。
另一个解决方法是用类型 class (MyTransform
) 和隐式转换 (myConversion
) 替换隐式转换 (X => Y
, KleisliOps
)根据此类型定义 class( 这有助于隐式转换)
trait MyTransform[X, Y] {
def transform(x: X): Y
}
implicit def myConversion[X, Y](x: X)(implicit mt: MyTransform[X, Y]): Y =
mt.transform(x)
type Partial[A, B] = A => Option[B]
implicit def partialToKleisliOps[A, B]: MyTransform[Partial[A, B], KleisliOps[A, B]] =
f1 => new KleisliOps(f1)
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)
}
尝试在 Scala 中为虚构的部分类型实现 Kleisli 类别(阅读 Bartosz Milewski 的“程序员的类别理论”,这是第 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) 没有显示任何错误,但是当我 运行 这个片段时,我得到:
Error:(27, 57) value >=> is not a member of $line5.$read.$iw.$iw.Kleisli.Partial[Double,Double]
val safeRootRecip: Partial[Double, Double] = safeRoot.>=>(safeRecip)
在隐式 class 之外定义 >=>
工作正常。可能是什么原因?
@sinanspd 是对的。在 Dotty 中,代码似乎可以编译: https://scastie.scala-lang.org/n17APWgMQkWqy93ct2cghw
手动解决
val safeRootRecip: Partial[Double, Double] = KleisliOps(safeRoot).>=>(safeRecip)
编译但编译器本身找不到此转换
Information: KleisliOps{<null>} is not a valid implicit value
for App.safeRoot.type => ?{def >=> : ?} because:
type mismatch;
found : App.safeRoot.type (with underlying type App.Partial[Double,Double])
required: App.Partial[A,Double]
(which expands to) A => Option[Double]
val safeRootRecip: Partial[Double, Double] = safeRoot.>=>(safeRecip)
似乎没有推断类型参数 A
。
(顺便说一下,Martin Odersky 在这里解释了为什么语言中存在隐式转换会使类型推断变得更糟:https://contributors.scala-lang.org/t/can-we-wean-scala-off-implicit-conversions/4388)
尝试使 Partial
与 B
协变,并且(特别是)与 A
协变(类似于 A => Option[B]
与 [= 协变) 16=] 和关于 A
)
type Partial[-A, +B] = A => Option[B]
然后代码似乎可以编译了。
另一个解决方法是用类型 class (MyTransform
) 和隐式转换 (myConversion
) 替换隐式转换 (X => Y
, KleisliOps
)根据此类型定义 class(
trait MyTransform[X, Y] {
def transform(x: X): Y
}
implicit def myConversion[X, Y](x: X)(implicit mt: MyTransform[X, Y]): Y =
mt.transform(x)
type Partial[A, B] = A => Option[B]
implicit def partialToKleisliOps[A, B]: MyTransform[Partial[A, B], KleisliOps[A, B]] =
f1 => new KleisliOps(f1)
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)
}