Scala 2.8で<:<、<%<、および=:=は何を意味し、それらはどこに文書化されていますか?


201

PredefのAPIドキュメントで、それらがジェネリック関数タイプ(From)=> Toのサブクラスであることがわかりますが、それだけです。えっと、何?ドキュメントはどこかにあるかもしれませんが、検索エンジンは "<:<"のような "名前"をうまく処理しないので、私はそれを見つけることができませんでした。

追加質問:これらのファンキーなシンボル/クラスを使用する必要があるのはなぜですか?


6
これは、少なくとも部分的にあなたの質問に答える可能性がある関連する質問です:stackoverflow.com/questions/2603003/operator-in-scala
Yardena

13
symbolhound.comはコード検索の友達です:)
ron

Haskellのtypeclassesはこれらのオペレーターの仕事を実行しますか?例:compare :: Ord a => a -> a -> Ordering?私はこのScalaの概念を、Haskellの対応物に関して理解しようとしています。
ケビンメレディス

回答:


217

これらは一般化された型制約と呼ばれます。これにより、型パラメーター化されたクラスまたは特性内から、型パラメーターの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 AStringです。あなたはと考えることができます証拠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ます。つまり、あなただけ呼び出すことができますgetStringLengthFoo[String]。この制約はコンパイル時に適用されます。これはすばらしいことです。

<:<そして、<%<同様に作業が、わずかなバリエーションを持ちます:

  • A =:= B Aは正確にBでなければならないことを意味します
  • A <:< BAはBのサブタイプでなければならないことを意味します(単純な型制約に<:
  • A <%< BAが表示可能でなければならないことを意味します Bとして。おそらく暗黙的な変換(単純型制約に類似<%

@retronymによるこのスニペットは、この種のことがどのようにして達成され、一般化された型制約がどのようにしてこれをより簡単にするのかについての良い説明です。

補遺

あなたのフォローアップの質問に答えるために、確かに、私が与えた例はかなり工夫されており、明らかに役に立たない。しかしList.sumInts、整数のリストを合計するメソッドのようなものを定義するためにそれを使用することを想像してください。このメソッドが古いListで呼び出されるのを許可する必要はありませんList[Int]。ただし、Listタイプコンストラクターをそれほど制約することはできません。あなたはまだ文字列、foos、bars、whatnotsのリストを持ちたいと思っています。したがって、一般化された型制約をに配置sumIntsすることで、そのメソッドだけがでのみ使用できる追加の制約を持つことを保証できますList[Int]。基本的に、特定の種類のリスト用に特別なケースのコードを書いています。


3
さて、わかりましたが、Manifestあなたが言及しなかった、の同じ名前のメソッドもあります。
ダニエルC.ソブラル2010

3
上の方法がManifestある<:<>:>OPは一般型制約の正確3つの品種を述べただけなので...、私は彼が興味を持ったものだと仮定しています。
トム・クロケット

12
@IttayD:これはかなり賢い...です。class =:=[From, To] extends From => Toつまり、typeの暗黙的な値From =:= Toは、実際にはからへの暗黙的な変換です。したがって、タイプの暗黙的なパラメータを受け入れることで、暗黙的にに変換できると言います。順序を変更し、暗黙の引数のタイプをにした場合、これはからへの暗黙の変換になるため、機能しません。FromToA =:= StringAStringString =:= AStringA
トムクロケット

25
これらの3文字のシンボルには名前がありますか?Scalaのシンボルスープに関する私の問題は、口頭で話すのが難しいことであり、Googleやその他の検索エンジンを使用して、それらの使用法の説明や例を見つけることは事実上不可能です。
ギガトロン、2011年

4
@Andreaいいえ、これは型が完全に等しい場合にのみ機能します。From =:= Toスコープに型の暗黙的な値があることは、暗黙的な変換があることを意味すると述べましたFrom => Toが、その意味は逆に実行されません。暗黙的な変換A => Bがあることは、のインスタンスがあることを意味しませA =:= B=:=は、で定義された封印された抽象クラスでありscala.Predef、暗黙的に公開されたタイプのインスタンスを1つだけ公開していますA =:= A。したがって、型の暗黙の値はA =:= BABが等しいという事実を目撃することが保証されます。
トムクロケット

55

完全な答えではありません(他の人はすでにこれに答えています)。構文をよりよく理解するのに役立つ次の点に注意したいと思います。たとえば、pelotomの例のように、これらの「演算子」を通常使用する方法:

def getStringLength(implicit evidence: A =:= String)

型演算子にScalaの代替中置構文を使用します

したがって、A =:= Stringと同じです=:=[A, String](そして=:=、派手な名前のクラスまたは特性です)。この構文は「通常の」クラスでも機能することに注意してください。たとえば、次のように記述できます。

val a: Tuple2[Int, String] = (1, "one")

このような:

val a: Int Tuple2 String = (1, "one")

これは、メソッド呼び出しの2つの構文である「通常の」と「.および」()演算子の構文に似ています。


2
ので、ニーズがupvote makes use of Scala's alternative infix syntax for type operators.完全に全部が意味を成さないことなく、この説明が不足している
OvidiuさんDolha

39

他の回答を読んで、これらの構成要素を理解してください。ここ、あなたがそれらを使用する必要があります。特定のタイプのメソッドのみを制約する必要がある場合に使用します。

例を示します。次のように、同種のペアを定義するとします。

class Pair[T](val first: T, val second: T)

次のsmallerように、メソッドを追加します。

def smaller = if (first < second) first else second

T注文した場合にのみ機能します。クラス全体を制限できます:

class Pair[T <: Ordered[T]](val first: T, val second: T)

しかし、それは残念なことのようです-がT注文されていないときにクラスが使用される可能性があります。型制約があっても、smallerメソッドを定義できます。

def smaller(implicit ev: T <:< Ordered[T]) = if (first < second) first else second

それは、たとえば、インスタンス化するには大丈夫だPair[File]限り、あなたは電話をしないよう smaller、それに。

の場合Option、実装者はorNullメソッドを望んでいましたが、それはには意味がありませんOption[Int]。型制約を使用することで、すべてがうまくいきます。あなたは使用することができるorNullOption[String]、あなたが形成できるOption[Int]と長い間、あなたが呼び出すことはありませんようとして、それを使用するorNullことに。しようSome(42).orNullとすると、魅力的なメッセージが表示されます

 error: Cannot prove that Null <:< Int

2
これはこの回答から数年後の<:<ことだと思いますが、の使用例を探しています。トレイトではなくタイプクラスをOrdered使用するので、この例はもうそれほど説得力がないと思います。ような何か:。OrderingOrdereddef smaller(implicit ord: Ordering[T]) = if (ord.lt(first, second)) first else second
ebruchez 2014

1
@ebruchez:ユースケースは、変更されていないScala

17

どこで使用されているかによります。ほとんどの場合、暗黙的なパラメーターの型を宣言するときに使用すると、それらはクラスになります。まれにオブジェクトになることもあります。最後に、Manifestオブジェクトの演算子にすることができます。それらは内部で定義されていますscala.Predef特に十分に文書化されていませんが、最初の2つのケースででています。

<:と同様に、クラス間の関係をテストする方法を提供することを目的としています<%、後者が使用できない状況でしています。

「いつ使うべきなのか」という質問については、知らない限り、使うべきではありません。:-) EDIT:OK、OK、ライブラリの例をいくつか示します。にEither、次のものがあります。

/**
  * Joins an <code>Either</code> through <code>Right</code>.
  */
 def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C] = this match {
   case Left(a)  => Left(a)
   case Right(b) => b
 }

 /**
  * Joins an <code>Either</code> through <code>Left</code>.
  */
 def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match {
   case Left(a)  => a
   case Right(b) => Right(b)
 }

Option、あなたは持っています:

def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null

コレクションには他にもいくつかの例があります。


ある:-)これらの別のもの?そして、「いつ使用すべきか」に対するあなたの回答にも同意します。非常に多くのものに適用されます。
Mike Millerが

「それらはクラス間の関係をテストする方法を提供することを意図しています」<-一般的すぎて
Jeff

3
「「いつ使用すべきか」という質問については、答えは、使用する必要があることを知らない限り、使用しないことです。」<-それが私が尋ねている理由です。自分でその決心ができるようになりたいです。
ジェフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.