这是我第二次尝试定义问题,我无法理解它。
我希望能够定义一个代数类型并在其上定义一个简单的类型类,比方说Show
。在haskell我做:
data Tree a = EmptyTree | Node a deriving (Show)
现在,如果我输入EmptyTree
- haskell可以显示它,那么它属于Show
。
现在我想在scala中做同样的事情:
sealed abstract class Tree[+T]
case object EmptyTree extends Tree[Nothing]
case class Node[T](value: T) extends Tree[T]
然后我在它周围定义Show
:
implicit def show[T] = Show.showA[Tree[T]]
我可以做println((EmptyTree : Tree[Int]).show)
。但我不能println(EmptyTree.show)
(回复是value show is not a member of object EmptyTree
)
我必须写下额外的内容:
implicit class MyShowOps[A, +T <: Tree[A]](t: T) {
def showMy(implicit ev: Show[Tree[A]]): String = ev.shows(t)
}
只有这样我才能做println(EmptyTree.showMy)
它仍然听起来不正确,我相信我要么做错了,我不应该像那样应用Show
并且应该仅将我的构造用作Tree[T]
或我我错过了Scalaz的正确建筑。
答案 0 :(得分:4)
Scala对ADT的表示与Haskell的不同之处在于它的构造函数有自己的类型。这部分是关于实际的互操作性 - 在JVM上使用子类型是很自然的 - 它有both advantages and disadvantages。
您遇到了一个缺点,即将值静态类型化为构造函数类型通常会使类型推断和隐式解析变得复杂。
类型类实例是静态解析的,在您的情况下,Show
具体不是逆变的,因此Tree[T]
的实例不是EmptyTree.type
的实例。从Scalaz的角度来看,最惯用的解决方案是提供返回ADT类型的智能构造函数:
import scalaz.Show, scalaz.syntax.show._
sealed abstract class Tree[+T]
object Tree {
private[this] case object EmptyTree extends Tree[Nothing]
private[this] case class Node[T](value: T) extends Tree[T]
val emptyTree: Tree[Nothing] = EmptyTree
def node[T](value: T): Tree[T] = Node(value)
implicit def show[T]: Show[Tree[T]] = Show.showA[Tree[T]]
}
现在你可以写Tree.emptyTree.show
。
请注意,即使是更简单的上下文,此问题也会出现。例如,假设我们想要折叠一个Option
作为累加器的列表:
scala> List(1, 2, 3).foldLeft(Some(0))((acc, i) => acc.map(_ + i))
<console>:11: error: type mismatch;
found : Option[Int]
required: Some[Int]
List(1, 2, 3).foldLeft(Some(0))((acc, i) => acc.map(_ + i))
^
由于Some(0)
的推断类型为Some[Int]
,而非Option[Int]
,因此foldLeft
方法推断的类型参数对{map
的结果限制过多1}}。
如果标准库为这样的情况提供Option.none
和Option.some
“构造函数”会很好,但它不会,所以你要么必须在第一个参数上放置一个类型注释或使用Scalaz的none
和some
:
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> List(1, 2, 3).foldLeft(some(0))((acc, i) => acc.map(_ + i))
res0: Option[Int] = Some(6)
在您的情况下,您可能会控制ADT定义,因此您可以自己提供这样的智能构造函数。