Scala 子类型 +隐式转换问题

发布于 2024-11-09 01:49:10 字数 1415 浏览 0 评论 0原文

我正在尝试使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

在风中等你 2024-11-16 01:49:10

发生这种情况是因为 Scalaz 没有将 Monad 定义为其第一个类型参数(或更准确地说,类型构造函数参数)中的协变。换句话说,Monad[A] 被视为与 Monad[B] 完全不同的类型,即使 A <: B 。 (有关协变和逆变的更多信息

Monad 有充分的理由 是不变的。一是:如果您让编译器相信 Monad[Id] 实际上也与 Monad[ExtendedId] 一样有效,那么您一定会在某些时候遇到问题要点 - 其中之一是,无论何时调用 pure,编译器都会推断 ExtendedId 结果类型,而只会返回 Id

认为没有一种技术可以彻底解决这个问题 - 除了定义一个Monad[ExtendedId],或者类似的东西

implicit def idMonad[A[_] <: Id[_]]: Monad[A] = ...

确实能够为所有的人返回一个正确的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, a Monad[A] is considered a different type altogether from a Monad[B], even if A <: B. (More on covariance and contravariance)

There are good reasons why Monad is invariant. One is: if you make the compiler believe that a Monad[Id] is actually also valid as a Monad[ExtendedId], you're bound to run into problems at some point — one of them is that wherever pure is called, the compiler will infer an ExtendedId result type, whereas only an Id would be returned.

I think there isn't a technique to fix this cleanly — other than defining a Monad[ExtendedId], or something like

implicit def idMonad[A[_] <: Id[_]]: Monad[A] = ...

which indeed is able to return a proper monad for all subclasses of Id.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文