2016-01-20 7 views
5

これは私が問題を定義するための2番目の試みです。スカラーズの類義語と継承

代数型を定義し、その上に単純な型定義を定義できるようにしたいとします。Showとしましょう。 Haskellで私は:私はEmptyTreeを入力すると今

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

、 - それはShowに属しているので、Haskellは、それを表示することができます。

今、私は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から適切な工事が行えません。

答えて

4

ADTのScalaの表現は、そのコンストラクタが独自の型を持っている点で、Haskell'sとは異なります。これは部分的に実用的な相互運用性に関するもので、サブタイプの使用はJVM上で自然であり、それにはboth advantages and disadvantagesがあります。

コンストラクタの型として静的に型指定された値を持つと、型の推論と暗黙の解決が複雑になることがあるという欠点があります。

Typeクラスのインスタンスは、静的に解決され、そしてあなたのケースでは、具体的ShowはそうTree[T]のインスタンスがEmptyTree.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]] 
} 

今、あなたはTree.emptyTree.showを書くことができます:Scalazの観点から最も慣用ソリューションは、ADT型を返すスマートなコンストラクタを提供することです。

この問題は、さらに単純なコンテキストでも発生することに注意してください。 Some(0)のための推論された型がSome[Int]、ないOption[Int]あるので、foldLeft方法のための推論の型パラメータがあまりにも限定的である

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

:たとえば、私たちはアキュムレータとしてOptionと、リストの上に折り畳むするとしますmapの結果

標準ライブラリは、このような場合のためにOption.noneOption.some「コンストラクタ」を提供する場合、それはいいだろうが、それはない、あなたが最初の引数に型注釈を入れたりScalazのnoneのようなものを使用しなければならないのいずれかようにsome:あなたの場合、あなた自身のようにスマートなコンストラクタを提供することができますので、あなたはおそらく、ADTの定義を制御するには

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) 

+0

私はscalazのソースで 'val emptyTree:Tree [Nothing] ..'のようなパターンを見たことがありますが、そのようにした理由を理解できませんでした。今私には明らかです。私が理解していないものがまだあります。私が行ったようにscalaz 'ShowOps'が定義されていなかった特別な理由はありますか? 'emptyTree'や' node [T] 'のようなラッパーメソッドを定義しなければならないよりも簡単です – Archeg

+0

アップストリームのコンストラクターパターンは、これらの問題の多くを1か所で解決しますが、サブタイプのインスタンスを提供すると、コードの一般的に、Scalazはサブタイプに対応するために抜け出すことはありません(ほとんどのタイプクラスは不変で、インスタンスはサブタイプには提供されません)。 –