Typeclasses and inheritance in scalaz

This is my second attempt to identify the problem, I can not get around it.

I want to be able to define an algebraic type and define a simple class above it, say Show . In haskell, I do:

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

Now, if I EmptyTree - haskell can show it, therefore 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 a Show around it:

 implicit def show[T] = Show.showA[Tree[T]] 

I can do println((EmptyTree : Tree[Int]).show) . But I can not do println(EmptyTree.show) (answer value show is not a member of object EmptyTree )

I need to write additionally:

 implicit class MyShowOps[A, +T <: Tree[A]](t: T) { def showMy(implicit ev: Show[Tree[A]]): String = ev.shows(t) } 

And only then can I do println(EmptyTree.showMy)

It still doesn’t sound right, I believe that I am trying to do the wrong thing, and I should not use Show like this, and should use my construction only as Tree[T] , or I am missing a proper construction from Scalaza.

+5
source share
1 answer

The Scala representation of ADT differs from Haskell in that its constructors have their own types. This is partly due to practical interoperability - using subtypes is natural on the JVM and has both advantages and disadvantages .

You encounter one of the drawbacks of having values ​​statically typed as constructor types often complicates type inference and implicit resolution.

The types of instances of the class are statically permitted, and in your case, Show not contravariant, so the instance for Tree[T] not an instance for EmptyTree.type . The most idiomatic solution from Scalaz's point of view is to provide intelligent 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 appears in even simpler contexts. For example, suppose we want to reset the list using Option as a battery:

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

Since the output type for Some(0) is Some[Int] and not Option[Int] , the type parameter that was specified for the foldLeft method is too restrictive for the map result.

It would be nice if the standard library provided Option.none and Option.some "constructors" for such cases, but this is not so, so you need to either put the type annotation on the first argument, or use something like Scalaz 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 apparently control the definition of ADT so that you can create smart constructors yourself.

+4
source

Source: https://habr.com/ru/post/1241058/


All Articles