サーチ…


備考

直列化の問題、特に分散環境( Apache Sparkなど )での問題を避けるため、型クラス・インスタンス用のSerializableな特性を実装することがベスト・プラクティスです。

シンプルタイプクラス

型クラスは、1つ以上の型パラメータを持つ単なるtraitです。

trait Show[A] {
  def show(a: A): String
}

型クラスを拡張する代わりに、サポートされている型ごとに型クラスの暗黙のインスタンスが用意されています。これらの実装を型クラスのコンパニオンオブジェクトに配置することで、特別なインポートをせずに暗黙の解決を行うことができます。

object Show {
  implicit val intShow: Show[Int] = new Show {
    def show(x: Int): String = x.toString
  }

  implicit val dateShow: Show[java.util.Date] = new Show {
    def show(x: java.util.Date): String = x.getTime.toString
  }

  // ..etc
}

関数に渡される汎用パラメータに型クラスのインスタンスがあることを保証したい場合は、暗黙のパラメータを使用します。

def log[A](a: A)(implicit showInstance: Show[A]): Unit = {
  println(showInstance.show(a))
}

コンテキストバインドを使用することもできます

def log[A: Show](a: A): Unit = {
  println(implicitly[Show[A]].show(a))
}

他の方法と同様に、上記のlogメソッドを呼び出しlog 。暗黙の場合はコンパイルに失敗しますShow[A]の実装が見つからないAあなたがに渡すlog

log(10) // prints: "10"
log(new java.util.Date(1469491668401L) // prints: "1469491668401"
log(List(1,2,3)) // fails to compile with
                 // could not find implicit value for evidence parameter of type Show[List[Int]]

この例では、 Show型のクラスを実装しています。これは、任意の型の任意のインスタンスをStringに変換するために使用される共通の型クラスです。すべてのオブジェクトが持っているにもかかわらずtoStringメソッドを、それがいるか否かは必ずしも明確ではないtoString便利な方法で定義されています。 Show型クラスを使用すると、 log渡されたものがStringへの明確な変換を持つことを保証することができlog

型クラスの拡張

この例では、以下の型クラスの拡張について説明します。

trait Show[A] {
  def show: String
}

あなたがコントロールするクラスを作成するには(そしてScalaで書かれています)、型クラスを拡張し、コンパニオンオブジェクトに暗黙的に追加します。 この例からPersonクラスを取得してShowを拡張する方法を示しましょう:

class Person(val fullName: String) {    
  def this(firstName: String, lastName: String) = this(s"$firstName $lastName")
}

このクラスは、 Personのコンパニオンオブジェクトに暗黙的に追加することで、 Showを拡張することができます:

object Person {
  implicit val personShow: Show[Person] = new Show {
    def show(p: Person): String = s"Person(${p.fullname})"
  }
}

コンパニオンオブジェクトはクラスと同じファイルになければならないので、 class Personobject Person両方が同じファイルに必要です。

Scalaで制御しないクラスを作成するには、 単純型クラスの例に示すように、型クラスを拡張し、型クラスのコンパニオンオブジェクトに暗黙的に追加します。

クラスも型クラスも制御しない場合は、上記のように暗黙的に作成してimportします。 シンプル・タイプ・クラスの例でlogメソッドの使用:

object MyShow {
  implicit val personShow: Show[Person] = new Show {
    def show(p: Person): String = s"Person(${p.fullname})"
  }
}

def logPeople(persons: Person*): Unit = {
  import MyShow.personShow
  persons foreach { p => log(p) }
}

型に型関数を追加する

Scalaの型クラスの実装はかなり冗長です。冗長性を減らす1つの方法は、いわゆる "操作クラス"を導入することです。これらのクラスは、インポート時に変数/値を自動的にラップして機能を拡張します。

これを説明するために、まず単純な型クラスを作成しましょう:

// The mathematical definition of "Addable" is "Semigroup"
trait Addable[A] {
  def add(x: A, y: A): A
}

次に、特性を実装します(型クラスをインスタンス化します)。

object Instances {

  // Instance for Int
  // Also called evidence object, meaning that this object saw that Int learned how to be added
  implicit object addableInt extends Addable[Int] {
    def add(x: Int, y: Int): Int = x + y
  }

  // Instance for String
  implicit object addableString extends Addable[String] {
    def add(x: String, y: String): String = x + y
  }

}

現時点では、Addableインスタンスを使用することは非常に面倒です。

import Instances._
val three = addableInt.add(1,2)

むしろ1.add(2)書くだけです。したがって、私たちはAddableを実装する型を常にラップする "Operation Class"( "Ops Class"とも呼ばれる)をAddableます。

object Ops {
  implicit class AddableOps[A](self: A)(implicit A: Addable[A]) {
    def add(other: A): A = A.add(self, other)
  }
}

今度は、新しい関数addを、それがIntString一部であるかのように使用できます。

object Main {

  import Instances._ // import evidence objects into this scope
  import Ops._       // import the wrappers

  def main(args: Array[String]): Unit = {

    println(1.add(5))
    println("mag".add("net"))
    // println(1.add(3.141)) // Fails because we didn't create an instance for Double

  }
}

"Ops"クラスは、 simulacrumライブラリのマクロによって自動的に作成されます:

import simulacrum._

@typeclass trait Addable[A] {
  @op("|+|") def add(x: A, y: A): A
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow