代码之家  ›  专栏  ›  技术社区  ›  Archeg

scalaz中的类型类和继承

  •  5
  • Archeg  · 技术社区  · 10 年前

    这是我第二次尝试定义这个问题,我想不通。

    我希望能够定义一个代数类型,并在上面定义一个简单的类型类,比如 Show 。在哈斯克尔,我做到了:

    data Tree a = EmptyTree | Node a deriving (Show)
    

    现在,如果我键入 EmptyTree -haskell可以展示它,所以它属于 显示 .

    现在,我正尝试在scala中做同样的事情:

    sealed abstract class Tree[+T]
    case object EmptyTree extends Tree[Nothing]
    case class Node[T](value: T) extends Tree[T]
    

    然后我定义 显示 围绕它:

    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)

    这听起来仍然不正确,我认为要么我试图做错事,要么我不应该申请 显示 像那样,应该只使用我的结构作为 Tree[T] 或者我缺少Scalaz的正确构造。

    1 回复  |  直到 10 年前
        1
  •  4
  •   Community Mohan Dere    9 年前

    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 .

    如果标准库提供 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定义,因此您可以自己提供这样的智能构造函数。