This is my second try to define the problem, I can't get my head around it.
I want to be able to define an algebraic type and define a simple typeclass over it, let's say Show
. In haskell I do:
data Tree a = EmptyTree | Node a deriving (Show)
Now, if I type EmptyTree
- haskell can show it, so it belongs to Show
.
Now I am trying to do the same in scala:
sealed abstract class Tree[+T]
case object EmptyTree extends Tree[Nothing]
case class Node[T](value: T) extends Tree[T]
Then I define Show
around it:
implicit def show[T] = Show.showA[Tree[T]]
I can do println((EmptyTree : Tree[Int]).show)
. But I can't do println(EmptyTree.show)
(response is value show is not a member of object EmptyTree
)
I have to write additional:
implicit class MyShowOps[A, +T <: Tree[A]](t: T) {
def showMy(implicit ev: Show[Tree[A]]): String = ev.shows(t)
}
And only then I can do println(EmptyTree.showMy)
It still doesn't sound correct, I believe either I am trying to do a wrong thing and I am not supposed to apply Show
like that and should use my construction only as Tree[T]
or I am missing a proper construction from Scalaz.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With