Scala 子类型 +隐式转换问题
我正在尝试使用 scalaz 库对 scala 中的 monad 进行一些操作,但在使其与子类型完美配合时遇到了一些麻烦。
我开始定义我自己的单子。为了简单起见,让它成为一个身份 monad:
import scalaz._
import Scalaz._
class Id[+A] (val value : A) { }
implicit object IdMonad extends Monad[Id] {
override def pure[A](a : => A) = new Id(a)
override def bind[A, B](a : Id[A], f : A => Id[B]) = f(a.value)
}
接下来,我用一些附加功能扩展了它:
class ExtendedId[A] (value : A, val printer : A => String) extends Id[A](value) { }
有了这个附加功能,ExtendedId
不再是一个 monad。
现在我想使用 ExtendedId[A]
类型的对象作为 Id[A]
:
def increment1(v : ExtendedId[Int]) : Id[Int] = {
for(v <- v) yield v + 1;
// ^
// error: could not find implicit value for parameter t: scalaz.Functor[test.package.ExtendedId]
}
请注意,我明白,因为 ExtendedId
不是一个 monad,我能得到的最好的输出是 Id[Int]
,我对此很满意!但不幸的是,该代码仍然无法编译。
然而,这个函数确实如此:
def asId[A](a : ExtendedId[A]) : Id[A] = a
def increment2(v : ExtendedId[Int]) {
for(v <- asId(v)) yield v + 1;
}
在这里,asId
函数只不过将其参数从 ExtendedId[A]
向上转换为 Id[A]
。看起来应该是完全多余的,其实不然。
为什么会发生这种情况?确实存在从 Id[A]
到包含 map
的对象的隐式转换,并且显然确实存在从 ExtendedId[A]
的简单隐式转换> 到 Id[A]
。那么,为什么编译器无法将它们结合起来呢?
I am trying to do something with monads in scala using scalaz library, and have some trouble making it work nicely with subtyping.
I have started with defining my own monad. Let it be an identity monad for the sake of simplicity:
import scalaz._
import Scalaz._
class Id[+A] (val value : A) { }
implicit object IdMonad extends Monad[Id] {
override def pure[A](a : => A) = new Id(a)
override def bind[A, B](a : Id[A], f : A => Id[B]) = f(a.value)
}
Next, I have extended it with some additional functionality:
class ExtendedId[A] (value : A, val printer : A => String) extends Id[A](value) { }
With this additional functionality, ExtendedId
is not a monad anymore.
Now I want to use the object of type ExtendedId[A]
as an Id[A]
:
def increment1(v : ExtendedId[Int]) : Id[Int] = {
for(v <- v) yield v + 1;
// ^
// error: could not find implicit value for parameter t: scalaz.Functor[test.package.ExtendedId]
}
Note that I understand that since ExtendedId
is not a monad, the best I can get as an output is Id[Int]
, and I am okay with that! But unfortunately, that code still does not compile.
However, this one does:
def asId[A](a : ExtendedId[A]) : Id[A] = a
def increment2(v : ExtendedId[Int]) {
for(v <- asId(v)) yield v + 1;
}
Here, asId
function does nothing more than upcasting its argument to from ExtendedId[A]
to Id[A]
. It seems that it should be completely redundant, but it is not.
Why is this happening? There does exist an implicit conversion from Id[A]
to an object containing map
and there obviously does exist a trivial implicit conversion from ExtendedId[A]
to Id[A]
. So, why is the compiler unable combine them?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
发生这种情况是因为 Scalaz 没有将 Monad 定义为其第一个类型参数(或更准确地说,类型构造函数参数)中的协变。换句话说,
Monad[A]
被视为与Monad[B]
完全不同的类型,即使A <: B
。 (有关协变和逆变的更多信息)Monad 有充分的理由
是不变的。一是:如果您让编译器相信Monad[Id]
实际上也与Monad[ExtendedId]
一样有效,那么您一定会在某些时候遇到问题要点 - 其中之一是,无论何时调用pure
,编译器都会推断ExtendedId
结果类型,而只会返回Id
。我认为没有一种技术可以彻底解决这个问题 - 除了定义一个
Monad[ExtendedId]
,或者类似的东西确实能够为所有的人返回一个正确的monad
Id
的子类。This happens because Scalaz does not define
Monad
as being covariant in its first type argument (or, more precisely, type constructor argument). In other words, aMonad[A]
is considered a different type altogether from aMonad[B]
, even ifA <: B
. (More on covariance and contravariance)There are good reasons why
Monad
is invariant. One is: if you make the compiler believe that aMonad[Id]
is actually also valid as aMonad[ExtendedId]
, you're bound to run into problems at some point — one of them is that whereverpure
is called, the compiler will infer anExtendedId
result type, whereas only anId
would be returned.I think there isn't a technique to fix this cleanly — other than defining a
Monad[ExtendedId]
, or something likewhich indeed is able to return a proper monad for all subclasses of
Id
.