スカラ。 皆のために夕暮れから抜け出しましょう!

そして今、あなたは吹く必要があります、あなたが吹かないならば、何も働かないからです。
-偉大な名言

そしてこんにちは!

今日は暗黙のScala言語についてお話します。 まだ推測していない人は、暗黙の変換、パラメーター、クラスなどについて話します。XenovExplicitを使用するすべての新規ユーザー、特にPythonの愛好家はImplicitよりも優れています。 1つの記事でコンパイラ全体と原則全体をカバーすることは不可能ですが、道は圧倒されますか?

1.暗黙の変換


そして、暗黙的な変換の比較的単純なセクションから始めます。 そして人生の例。
ヴァシリーは、メーカーのルノーの車を望んでいます。 家族全員が長い間お金を節約していましたが、彼らは全額を貯めることができませんでした。 新しいWHAに十分なお金のみ。 そして、突然拍手! ルノーがAvtoVAZを買収。 製造業者が今必要であり、十分なお金があるようです。 そして、暗黙のうちに、ヴァシャは外国車の幸せな所有者になりました。
次に、これをコードの形式で形式化しようとします。

人生の例
case class Vasiliy(auto: Renault) { println("Vasiliy owns "+auto) } case class Renault(isRussian: Boolean = false) case class VAZ(isRussian: Boolean = true) object VAZ { implicit def vaz2renault(vaz: VAZ): Renault = Renault(vaz.isRussian) //   } object Family { def present = { Vasiliy(VAZ()) //  .   ! } } 

Family.presentの実行の結果、 Vasiliy owns Renault(true)する行が表示されVasiliy owns Renault(true) 。 これがScalaがこの困難な生活の中で普通の人々を助ける方法です!
もっとプログラム的な例を挙げれば(私のプロジェクトで似たようなものを使用します):

生気のない例
 case class PermissionsList(permissions: Set[String] = Set("UL")); object PermissionsList { implicit def str2permissions(str: String) = PermissionsList(str.split(";").toSet) implicit def permissions2str(p: PermissionsList) = p.permissions.mkString(";") } // case class User(login: String, permissions: PermissionsList) /* somewhere in a galaxy far far away */ User(login = "Vasiliy", permissions = "UL;AL") //       

上記のコードにより、文字列を暗黙的にアクセスオブジェクトにキャストしたり、その逆を行うことができます。 これは、同じWeb上で作業するときに便利です。クライアントに"UL;AL"という形式の目的の行を"UL;AL"付けて、サーバーに送信するだけで、適切なタイミングでオブジェクトに変換されます。
そして、私たちは重要なポイントに来ました 。 VAZ カボチャがいつどのような条件下でルノーになり、文字列がPermissionsListオブジェクトになりますか?

ほとんどの場合、Scalaのすべての魔法はコンパイル時に発生します(言語は強く型付けされています)。 ローカルコンパイラは非常に賢く、機知に富んだ生き物です。 存在しなかったVAZクラスのインスタンスでexat()メソッドを呼び出そうとするとすぐに、コンパイラーは真剣に起動し metaをクックして VAZのexat()が実行可能なものへの暗黙的な変換を探します。 つまり、 implicit def a2b(a: A): B
彼は暗黙的な変換を探しています:
ところで、存在しないメソッドを呼び出すだけでなく、コンパイラは、異なるデータ型のパラメーターを必要とするメソッド/関数にオブジェクトを渡そうとすると、オブジェクトを変換しようとします。 これは、ヴァシリーと彼の家族の例からです。

1.1暗黙のクラス


バージョン2.10から、暗黙クラスがScalaに登場し、既存クラスの拡張機能を簡単にグループ化(メソッドを追加)できるようになりました。 以下に簡単な例を示します。

 object MySimpleHelper { implicit class StringExtended(str: String) { def sayIt = println(str) def sayItLouderBitch = println(str.toUpperCase +"!!!") } } 

引用された生データからわかるように、単一の引数(文字列)をとるオブジェクト内で宣言されたクラスがあります。 この行は、クラスメソッドの断片に引き裂かれるように与えられています。 そして、このことは単純に苦しめられます。

 import MySimpleHelper._ "oh gosh" sayIt > oh gosh "oh gosh" sayItLouderBitch > OH GOSH!!! 

ただし、ここで注意しなければならない制限がいくつかあります。

実際、 StringExtendedはコンパイラによって次のように変換されます。

 class StringExtended(str: String) { def sayIt = println(str) def sayItLouderBitch = println(str.toUpperCase +"!!!") } implicit def String2StringExtended(str: String): StringExtended = new StringExtended(str) 

おなじみですか?

2.暗黙のパラメーター


どういうわけか、すべてが単純すぎて、あなたはすでに退屈していますか? それは少しハードコアの時間です! 脳を動かして岩の根源に登りましょう:

Surlyコード
 /** * TraversableOnce.scala: minBy * ,  ,      ,      ,    ,    B    A . ,   B    ,    A,  B  . - . */ def minBy[B](f: A => B)(implicit cmp: Ordering[B]): A = { //   -   ? if (isEmpty) throw new UnsupportedOperationException("empty.minBy") //     var minF: B = null.asInstanceOf[B] var minElem: A = null.asInstanceOf[A] var first = true //    //   for (elem <- self) { //    A,   B val fx = f(elem) if (first || cmp.lt(fx, minF)) { //     -     . //  cmp.lt  true   ,  f: B <   minF: B minElem = elem minF = fx first = false } } minElem } 

プッシュされた、それはすべてが明らかであるようだ。
ストップ、2番目。 ただし、次のようなminByを使用します。
 val cities = Seq(new City(population = 100000), new City(500000)) val smallCity = cities.minBy( city => city.population ) 

そして、 cmp: Ordering[B] ( B == Int)渡しませんcmp: Ordering[B] ( B == Int) 。 コードは機能しているように見えますが...リラックスしてください。 これは魔法です。
インポートされたスコープ、特にscala.math.Ordering存在する

そのようなコード
 object Ordering extends LowPriorityOrderingImplicits { ... trait IntOrdering extends Ordering[Int] { def compare(x: Int, y: Int) = if (x < y) -1 else if (x == y) 0 else 1 } implicit object Int extends IntOrdering ... } 

最後の行に注意しましょう-IntOrdering IntOrdering Ordering[Int]継承するときに実装されるcompareメソッドを含む暗黙のIntオブジェクトがありIntOrdering 。 実際、このオブジェクトは比較のために使用され、暗黙的に不運なminByます。
非常に単純化された例は次のようになります。

フレンドリーコード
 implicit val myValue: Int = 5 object Jules { def doesHeLookLikeABitch(answer: String)(implicit times: Int) = { for(x <- 1 to times) println(answer ) } } Jules.doesHeLookLikeABitch("WHAT?") >WHAT? >WHAT? >WHAT? >WHAT? >WHAT? 

もちろん、ハンドルによって暗黙的なパラメーターを転送することを禁止する人はいません。 いいえ、まあ、突然必要になります。
そして、繰り返しますが、制限はありません。

3.ビュー/コンテキストの境界


脳がすでに溶けている場合は、休憩してコーヒーを飲むか、何か強いものを飲んでください。 今日の最新の非自明性についてお話します。
新しい車( exat()車)を運転するVasilyが成功した人、つまりプログラマーになったとしましょう。 そして今、VasilyはScalaについて書いており、彼はもう1つのSUGAR ARRHHを望んでいました。 マーティンは考えて言った-わかりました。 そして、それらにタイプと制限を導入しました。 同じdef f[T](a: T)

3.1ビューの境界

型を宣言するときのこの制限は、コンパイラに真の暗黙的な変換がどこかに近いことを伝えます。

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

つまり アクセス可能なスコープでAからBへの暗黙的な変換があります。 原則として、次の形式のエントリを想像できます。

 def f[A](a: A) (implicit a2b: A => B) = a.bMethod: 

ロシア人に近い例は次のようになります。

 class Car { def exat() = println("POEXALI") } class VAZ object VAZ { implicit def vaz2car(v: VAZ): Car = new Car() } def go[A <% Car](a: A) = a.exat() go(new VAZ()) > POEXALI 

いいね! 人生は美しくなり、髪は元に戻り、妻は戻ってきました。
しかし、ヴァシリーはそれを求めました、そして、マーティンはもう止められませんでした...それで、それは現れました

3.2コンテキスト境界

この制限はScala 2.8で導入されたもので、View Boundsとは異なり、暗黙的な変換には責任を負いませんが、暗黙的なパラメーターには責任があります

 def f[A : B](a: A) = g(a) //  g    B[A] 

最も単純な例は、そのようなペアです。

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

実際、これはHaskellと彼のタイプクラスパターンの遠いあいさつです。

あとがきの代わりに


道はそれで終わりではありません;長い間Scalaについて話すことができます。 しかし、一度にすべてではありません。なぜなら、主なことは理解と理解への欲求だからです。 欲求には、何が起こっているのかが認識されます。
この記事を読んだ後、暗黙的な変換を使用するプロジェクトのコードを理解できない場合は、メモ帳を閉じて通常のIDEを開くことをお勧めします。 そして、私の頭は沸騰しません、私は行きます。 ご清聴ありがとうございました。

Source: https://habr.com/ru/post/J209850/


All Articles