1. ホーム
  2. スカラ

[解決済み】Scalaのコンテキストとビューバウンドとは何ですか?

2022-04-16 09:47:14

質問

コンテキストバウンズとビューバウンズとは何か、その違いは何か、わかりやすく教えてください。

わかりやすい例もあるといいですね。

どのように解決するのですか?

この質問はすでにされていると思ったのですが、もしそうなら、その質問は"related"のバーには表示されていません。そこで、ここに紹介します。

ビューバウンドとは何ですか?

A ビューバウンド は、Scala で導入されたメカニズムで、ある種の A あたかも それは、ある種の B . 典型的な構文はこうです。

def f[A <% B](a: A) = a.bMethod

言い換えると A への暗黙の変換が必要です。 B を呼び出すことができるようになります。 B 型のオブジェクトに対するメソッド A . 標準ライブラリにおけるビュー境界の最も一般的な使い方(Scala 2.8.0以前はとにかく)は Ordered のような、このような。

def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b

を変換することができますので AOrdered[A] であり、また Ordered[A] はメソッドを定義しています。 <(other: A): Boolean という式が使えます。 a < b .

ご注意ください ビューバウンドは非推奨です そのため、避けてください。

コンテキストバウンドとは何ですか?

コンテキストバウンズはScala 2.8.0で導入され、通常、いわゆる 型クラスパターン Haskellの型クラスが提供する機能を、より冗長な方法でエミュレートしたコードパターンである。

ビューバウンドは単純な型に使用できますが(例えば。 A <% String ) が、コンテキストバウンドでは パラメータ化された型 のような Ordered[A] とは異なり String .

コンテキストバウンドは、暗黙の の代わりに、ビューバウンドの暗黙の 変換 . これは、ある型に対して、次のように宣言するために使用されます。 A という型の暗黙の値があります。 B[A] が利用できます。構文は次のようになります。

def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]

これは、ビューバウンドよりも使い方がすぐには分からないので、混乱する。Scalaでよくある使用例はこうだ。

def f[A : ClassManifest](n: Int) = new Array[A](n)

An Array パラメータ化された型の初期化には ClassManifest というのは、型の消去と配列の非消去の性質に関連した難解な理由があるからです。

ライブラリによくあるもう一つの例は、もう少し複雑です。

def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)

ここです。 implicitly は、欲しい暗黙の値を取得するために使われます。 Ordering[A] というメソッドを定義しているクラスです。 compare(a: A, b: A): Int .

以下、別の方法について見ていきます。

View BoundsとContext Boundsはどのように実装されていますか?

ビュー境界とコンテキスト境界の両方が暗黙のパラメータで実装されていることは、その定義からして驚くには値しないでしょう。実は、私が示した構文は、実際に何が起こるかを示す構文上の糖分なのです。どのように糖分を取り除くかは、以下をご覧ください。

def f[A <% B](a: A) = a.bMethod
def f[A](a: A)(implicit ev: A => B) = a.bMethod

def g[A : B](a: A) = h(a)
def g[A](a: A)(implicit ev: B[A]) = h(a)

そのため、当然ながら完全な構文で書くことができ、特にコンテキスト・バウンディングに有効である。

def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)

ビューバウンドは何に使うのですか?

ビューバウンドは、主に次のような目的で使用されます。 pimp my library これは、既存のクラスにメソッドを追加するパターンですが、元の型を何らかの形で返したい場合に使用します。もし、その型を返す必要がないのであれば、ビューバウンドは必要ありません。

ビューバインドの典型的な使用例としては Ordered . ただし IntOrdered 例えば、暗黙のうちに変換が行われているにもかかわらず。先に示した例では、変換されない型を返しているので、ビューバインドが必要です。

def f[A <% Ordered[A]](a: A, b: A): A = if (a < b) a else b

この例は、ビューバウンドがないと動きません。しかし、もし別の型を返すのであれば、もうビューバウンドは必要ありません。

def f[A](a: Ordered[A], b: A): Boolean = a < b

ここでの変換は、(必要であれば)パラメータを f ということで f は知る必要がない。

そのほか Ordered このライブラリで最もよく使われるのは StringArray は,Scalaのコレクションと同じようにJavaのクラスである.例えば

def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b

これをビューバウンドなしで行おうとすると StringWrappedString (Scala 2.8)、同様に Array .

戻り値の型のパラメータとしてのみ使用する場合でも、同様のことが起こります。

def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted

Context Boundsは何に使うのですか?

コンテキストバウンズは、主に以下のような用途で使用されます。 型クラスパターン Haskellの型クラスへの言及として。基本的にこのパターンは、暗黙のアダプターパターンのようなもので機能を利用できるようにすることで、継承に代わるものを実装しています。

典型的な例は、Scala 2.8 の Ordering を置き換えたものです。 Ordered Scala のライブラリ全体に適用されます。使い方は

def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b

しかし、通常はこのように書かれているのを見かける。

def f[A](a: A, b: A)(implicit ord: Ordering[A]) = {
    import ord.mkOrderingOps
    if (a < b) a else b
}

の中のいくつかの暗黙の変換を利用する。 Ordering で、伝統的な演算子スタイルを可能にします。Scala 2.8でのもう一つの例として Numeric :

def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)

より複雑な例として、新しいコレクションの使用法である CanBuildFrom しかし、それについてはすでに非常に長い回答があるので、ここでは避けます。また、前述したように ClassManifest これは具象型を持たない新しい配列を初期化するために必要です。

型クラスパターンによるコンテキストバウンドは、関心事の分離を可能にするため、自分のクラスで使われる可能性が高いが、ビューバウンドは良い設計によって自分のコードで回避できる(他人の設計を回避するために使われることがほとんどである)。

コンテキストバウンズは以前から可能でしたが、2010年に本格的に普及し、現在ではScalaの最も重要なライブラリやフレームワークのほとんどで、ある程度使用されています。ScalazライブラリはHaskellのパワーをScalaに取り入れたもので、その使い方の最も極端な例だ。 型クラスパターンを読んで、型クラスパターンの使い方にもっと詳しくなることをお勧めする。

EDIT

気になる関連質問