2016-02-29 19 views
5

A型クラス例:Scalaでtypeclassesを作成する方法はいくつかありますか?プログラミングScalaの本から取ら

// src/main/scala/progscala2/implicits/toJSON-type-class.sc 

case class Address(street: String, city: String) 
case class Person(name: String, address: Address) 

trait ToJSON[A] { 
    def toJSON(a: A, level: Int = 0): String 

    val INDENTATION = " " 
    def indentation(level: Int = 0): (String,String) = 
    (INDENTATION * level, INDENTATION * (level+1)) 
} 

object ToJSON { 
    implicit def addressToJson: ToJSON[Address] = new ToJSON[Address] { 
    override def toJSON(address: Address, level: Int = 0) : String = { 
      val (outdent, indent) = indentation(level) 
      s"""{ 
       |${indent}"street": "${address.street}", 
       |${indent}"city": "${address.city}" 
       |$outdent}""".stripMargin 
    } 
    } 
    implicit def personToJson: ToJSON[Person] = new ToJSON[Person] { 
    override def toJSON(a: Person, level: Int): String = { 
      val (outdent, indent) = indentation(level) 
      s"""{ 
       |${indent}"name": "${a.name}", 
       |${indent}"address": ${implicitly[ToJSON[Address]].toJSON(a.address, level + 1)} 
       |$outdent}""".stripMargin 
    } 
    } 
    def toJSON[A](a: A, level: Int = 0)(implicit ev: ToJSON[A]) = { 
    ev.toJSON(a, level) 
    } 
} 


val a = Address("1 Scala Lane", "Anytown") 
val p = Person("Buck Trends", a) 


import ToJSON.toJSON 
println(toJSON(a)) 
println(toJSON(p)) 

case class Address(street: String, city: String) 
case class Person(name: String, address: Address) 

trait ToJSON { 
    def toJSON(level: Int = 0): String 

    val INDENTATION = " " 
    def indentation(level: Int = 0): (String,String) = 
    (INDENTATION * level, INDENTATION * (level+1)) 
} 

implicit class AddressToJSON(address: Address) extends ToJSON { 
    def toJSON(level: Int = 0): String = { 
    val (outdent, indent) = indentation(level) 
    s"""{ 
     |${indent}"street": "${address.street}", 
     |${indent}"city": "${address.city}" 
     |$outdent}""".stripMargin 
    } 
} 

implicit class PersonToJSON(person: Person) extends ToJSON { 
    def toJSON(level: Int = 0): String = { 
    val (outdent, indent) = indentation(level) 
    s"""{ 
     |${indent}"name": "${person.name}", 
     |${indent}"address": ${person.address.toJSON(level + 1)} 
     |$outdent}""".stripMargin 
    } 
} 

val a = Address("1 Scala Lane", "Anytown") 
val p = Person("Buck Trends", a) 

println(a.toJSON()) 
println() 
println(p.toJSON()) 

型クラスは通常、Scalaでは、このように行われていることをコードが正常に動作しますが、私は(いくつかのブログの記事から)印象でいます

どちらの方が正しいか、どちらが正しいですか?どんな洞察も歓迎です。

+0

おそらく、プログラマーのコミュニティでこの質問をすることができます:http://programmers.stackexchange.com/ – ManoDestra

+1

@ManoDestra - これは主に意見に基づいてプログラマーが閉鎖する可能性が高いです。このような多くの(ほとんどの)質問は、「アプローチを選んで一貫性を持たせる」という答えに至ります。 – GlenH7

+0

真。ちょうど固体実装ではなく、プログラミング固有の質問のように思えます。 – ManoDestra

答えて

18

最初のToJSONを「型クラス」と呼ぶのはやっぱりですが(これらの用語は標準化されているわけではありませんが、2番目のより多くの重要な点では、 )。

私が定義すると考えるタイプクラスのプロパティの1つは、ジェネリックタイプを制約できるということです。 Scalaはこれをコンテキスト境界の形でサポートする特別な構文を提供しています。次

import io.circe.Encoder 

def foo[A: Numeric: Encoder](a: A) = ... 

これは、両方のNumericEncoderインスタンスを持っているタイプAを制約します。

この構文は最初のToJSONでは使用できません。ビュー境界(現在は非推奨)や暗黙的な暗黙的な変換パラメータのようなものを使用する必要があります。

最初のToJSONスタイルでは提供できない操作も多数あります。たとえば、私たちは、型クラスの標準Scalaのエンコーディングを使用していますMonoid持っていると仮定します。

trait Monoid[A] { 
    def empty: A 
    def plus(x: A, y: A): A 
} 

をそして、我々は我々が対象となりますunparametrized Monoid形質を持っている最初のスタイル、にそれを翻訳したいです私たちがモノイドとして扱うことができるようにしたいタイプからの暗黙的な変換の私たちはemptyplusのシグネチャを参照できるタイプのパラメータがないので、私たちは全く運が悪いです。

他の引数の1つ:標準ライブラリ(OrderingCanBuildFromなど)のタイプクラスはすべて、第2のスタイルを使用します。これは、多数のサードパーティ製のScalaライブラリと同様です。

要するに、最初のバージョンは使用しないでください。 A => Whatever(ある具体的にはWhatever)の形式の操作しか持たず、優れた構文サポートがなく、一般的にはコミュニティによって慣用的ではないと考えられる場合にのみ機能します。

関連する問題