Typeclasses and inheritance in scalaz

不想你离开。 提交于 2019-11-30 20:57:08
Travis Brown

Scala's representation of ADTs differs from Haskell's in that its constructors have their own types. This is partly about practical interoperability—using subtyping is natural on the JVM—and it has both advantages and disadvantages.

You're running into one of the disadvantages, which is that having values that are statically typed as a constructor type often complicates type inference and implicit resolution.

Type class instances are statically resolved, and in your case specifically Show isn't contravariant, so an instance for Tree[T] isn't an instance for EmptyTree.type. The most idiomatic solution from the Scalaz perspective is to provide smart constructors that return the ADT type:

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]]
}

Now you can write Tree.emptyTree.show.

Note that this problem also turns up in even simpler contexts. For example, suppose we want to fold over a list with an Option as the accumulator:

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))
                                                          ^

Because the inferred type for Some(0) is Some[Int], not Option[Int], the type parameter that's inferred for the foldLeft method is too restrictive for the result of the map.

It would be nice if the standard library provided Option.none and Option.some "constructors" for cases like this, but it doesn't, so you either have to put a type annotation on the first argument or use something like Scalaz's none and 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)

In your case you presumably control the ADT definition, so you can provide smart constructors like this yourself.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!