いいものをつくろう

CTOの日記

Scala

scalaの難しい文法をスルーするのを辞めたらscalaレベルが上がった

更新日:

 

こんにちは

これまでスルーしてきた、難しめの文法を

適当に読まずにきちんと理解したいと思います。

箇条書きに書いていきますので、参考になるものがあれば幸いです。

きちんと理解すると、ソースコードへの恐れや・若干のあいまいさを残すことなく丁寧に読めるようになると

感じております。

 

アノテーション @transient って何

quora, What is @transient lazy val in Scala?

答えはモーガンスタンレーのアシシュさんがquraで解答されているように、シリアライズしたくないと。理由は、シリアライズするってことは

ネットワーク越しに送るってことであって、それをしたくないってこと。送りたくない理由はレイテンシーだったり、結果を再計算が大変だったり?ステートがわからなくなったりで

様々だと思う。以下は引用

Ashish Kumar Singh, Manager Big Data at Morgan Stanley (2017-present)Answered Nov 2, 2018

“@transient lazy val pattern in scala”

Given a you have a Scala object holding some data that you want to store or send around by serializing the object. It turns out that the object is also capable of performing some complex logic and it stores the results of these calculations in its field values. While it might be efficient to store the calculation results in memory for later lookup, it might be a bad idea to also serialize these fields as this will consume space you do not want to sacrifice or as this will increase network throughput (e.g., in Spark) resulting in more time being consumed than it requires to recalculate the fields. Now one could write a custom serializer for this task, but let us be honest: thats not really the thing we want to spent our time on.

This is where the @transient lazy val pattern comes in. In Scala lazy valdenotes a field that will only be calculated once it is accessed for the first time and is then stored for future reference. With @transient on the other hand one can denote a field that shall not be serialized.

参考:scalaアノテーション、 Scala一人巡りアノテーション(Annotations) in Scala日本語情報サイト

Predefの→はtuple2を返します

こんな関数が書かれていて、パラメーターのところに -> という見慣れない記号、

いや、マップの時に使うやつか。が使われていた。

この意味はscala.Predefに定義された暗黙の型変換 であることがわかった

val str: String = "hello"
import org.apache.spark.sql.types.StringType
val foo2 = foo(str -> StringType)

 

でPredefには以下のように書かれており、A, Bのタプルで返していることがわかる

/** @group implicit-classes-any */
implicit final class ArrowAssoc[A](private val self: A) extends AnyVal {
  @inline def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y)
  def →[B](y: B): Tuple2[A, B] = ->(y)
}

 

繰り返しパラメター by args: String*

stackoverflow, In scala can I pass repeated parameters to other methods?

scala> def bar(args:String*) = println("count="+args.length)
bar: (args: String*)Unit
 
scala> def foo(args:String*) = bar(args: _*)
foo: (args: String*)Unit
 
scala> foo("1", "2")
count=2

型パラメータ(javaでいうジェネリック)

こういう class Life[T <: School]  のです

関数とかクラスを特定の型で縛るのではなく、この型でもこの型でも、使えるようにしたものです。

つまりは

より汎用的なのです。

https://www.ne.jp/asahi/hishidama/home/tech/scala/generics.html

よく迷う

上限境界と下限境界をマスターしましょう。Scalaジェネリクス(パラメータ化された型)が参考になります

Building  <-  School <- Kindergarten

というよに継承されたクラスをかんがます。幼稚園がもっとも具現化されたクラスで、ビルが大元の親クラスですね。

class Life[T <: School] となった時は、これは上限(型パラメーターの境界)です

TはSchool以下のようも読めますね。T <= School つまりSchoolがもっとも上の親で、お願いします。ということです。

Building

School

Kindergarten

と考えるとよい、ですし、これは自然なクラス継承の図を表していますよね。

`val a = new Life[School]()` のようにできますし

`val a = new Life[Kindergarten]()` のようにできますし

だけどSchoolより上のBuildingはだめなので

`val a = new Life[Building]()` のようにできない

ということです。

反対の下限は、その逆で

class Life[T >: School] となった時は、これは下限(型パラメーターの境界)です

TはSchool以上のようも読めますね。T >= School つまりSchoolがもっとも下で、お願いします。ということです。

Building

School

Kindergarten

と考えるとよい、ですし、これは自然なクラス継承の図を表していますよね。

`val a = new Life[School]()` のようにできますし

`val a = new Life[Building]()` のようにできますし

だけどSchoolより上のBuildingはだめなので

`val a = new Life[Kindergarten]()` のようにできない

ということです。

上限下限の考え方と、テンプレート化はこれだけです。

とここまでは

継承における、継承関係をみてきました。それが上限・下限境界でした。

変位指定アノテーション(代入の方向性を決める共変・非変・反変)

こういう class Life[ +T ] のです

次は

ついでに、

代入における、継承関係をみましょう。それが変位指定アノテーションです。

共変(variant)と非変(nonvariant)、反変(covariant)とか馴染みのないやつです。

一旦、上限・下限境界で見た

Building

School

Kindergarten

の関係を覚えておけば簡単なはなしなので頑張ってください。

Scalaはディフォルトでは非変(nonvariant)です

`class Life[ +T ] `とした場合、Tの共変(variant)となり、子クラスを持つLifeを、親クラスを持つLifeに代入することができます。

`var a = new List[School]()` としたときに

`a = new Life[Kindergarten]()` のようにできますし

親は子供を受け入れることができる

と私は覚えています。

だけどBuildingはSchoolの親なので、Schoolを保持していたaは、自分の親は支えることができません。

`a = new Life[Building]()` のようにできない

反変(covariant)はその全く逆なだけです

という感じです。+ - がこの場合直感的ではないので、単純に、覚えましょう。

+は親は子供を受け入れることができる

-は子は親を受け入れることができるが、子供は無理

世の中的には、- のような世界が現実だと、社会問題になる気がするし、マイナス(-)な印象をもちますね

私は記憶しています。

補足

補足1ですが<:<: な怪しい記号をみらたら、型パラメーターの制約 [2011-07-24]

とういうコンパイラー機能のおけげでtoMapは特定の型だけ!とコンパイラが怒ってくれるようにできるんです。

補足2、javaでは全てが共変らしい。それゆえ危険らしい

一方、scalaではディフォルトで全てが非変です。

 

 T : A : ClassTag はcontext bound らしいです

またまた見慣れない文法。scalaのこういうハッキーなところが嫌です。

もっと簡単にしてよ〜、と思っています。

暗黙の引数implicitパラメータでよく使われるらしいので
とりあえず暗黙の引数の復習です。で特定のパターンでシンタックスシュガーがつかえるようです。
それがcontext_boundっぽい。
こちらにハンズオンな説明が
されていますが、
私にとってもはこのシンタックスシュガー まぎらわしいだけ。
ClassTagに関しては型消去という概念がはいってきて結果、したのよな書き方になるそうです

import scala.reflect.ClassTag

def isType[T <: A : ClassTag](a: A) = a match {
case t: T => true
case _ => false
}

 

 

 

 

以上です

-Scala

Copyright© CTOの日記 , 2020 All Rights Reserved.