これらは一般化された型制約と呼ばれます。これにより、型パラメーター化されたクラスまたは特性内から、型パラメーターの1つをさらに制約できます。次に例を示します。
case class Foo[A](a:A) { // 'A' can be substituted with any type
// getStringLength can only be used if this is a Foo[String]
def getStringLength(implicit evidence: A =:= String) = a.length
}
暗黙の引数evidence
はコンパイラーによって提供され、iff A
はString
です。あなたはと考えることができます証拠A
であるString
--the引数自体は唯一それが存在することを知って、重要ではありません。[編集:ええと、技術的には、からA
への暗黙的な変換を表すため、実際には重要です。これString
により、呼び出しa.length
を許可し、コンパイラーに怒鳴られることはありません]
これで次のように使用できます:
scala> Foo("blah").getStringLength
res6: Int = 4
しかし、私がFoo
それ以外のものを含むものでそれを使用しようとした場合String
:
scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]
あなたはそのエラーを「Int == Stringであるという証拠を見つけることができませんでした」と読むことができます... さらなる制限getStringLength
を課しているのタイプに、一般に必要A
なものよりもをFoo
ます。つまり、あなただけ呼び出すことができますgetStringLength
にFoo[String]
。この制約はコンパイル時に適用されます。これはすばらしいことです。
<:<
そして、<%<
同様に作業が、わずかなバリエーションを持ちます:
A =:= B
Aは正確にBでなければならないことを意味します
A <:< B
AはBのサブタイプでなければならないことを意味します(単純な型制約に<:
)
A <%< B
Aが表示可能でなければならないことを意味します Bとして。おそらく暗黙的な変換(単純型制約に類似<%
)
@retronymによるこのスニペットは、この種のことがどのようにして達成され、一般化された型制約がどのようにしてこれをより簡単にするのかについての良い説明です。
補遺
あなたのフォローアップの質問に答えるために、確かに、私が与えた例はかなり工夫されており、明らかに役に立たない。しかしList.sumInts
、整数のリストを合計するメソッドのようなものを定義するためにそれを使用することを想像してください。このメソッドが古いList
で呼び出されるのを許可する必要はありませんList[Int]
。ただし、List
タイプコンストラクターをそれほど制約することはできません。あなたはまだ文字列、foos、bars、whatnotsのリストを持ちたいと思っています。したがって、一般化された型制約をに配置sumInts
することで、そのメソッドだけがでのみ使用できる追加の制約を持つことを保証できますList[Int]
。基本的に、特定の種類のリスト用に特別なケースのコードを書いています。