Tips05(ドワンゴ社内 scala勉強会その1)
ドワンゴ社内 scala勉強会 2011/11/20
ttp://www.nicovideo.jp/watch/1322200991
yuroyoroさん
「クリエイティブで高品質なScalaプログラマになるための10のTips」
Optionメソッド
opt.getOrElse(デフォルト値) 指定されたデフォルト値
中間データ
filterしてmapすると中間データが出来る
withFilterを用いると中間データが出来ない.
l withFilter {_%2 == 0} map{_*2}
副作用が無いならfilterよりはwithFilterを使うべき.
strictとnon-strict
リストやマップのコレクションにはstrictとnon-strictがある
strictは操作毎(filter,slice,…)に中間データを作成する
viewはstrictなCollectionをnon-strictに変換する->中間データが出来ない!
col map(f1) map(f2) filter(p)
=> (col.view map(f1) map(f2) filter(p)).force
forceをするとデータを作成する.
標準Collectionはstrict -> viewする事でnon-strict -> forceでデータ抽出
whileとStream
Streamは遅延評価されるnon-strictなListである
val randomStream = Stream.continually{util.Random.nextPrintableChar}
必要分だけ作成する randomStream(5) -> 0~5だけ.
whileで処理fの結果が条件pを満たすまでfの結果を集める
Stream.continually{ f } takeWhile{ p }
Streamは要素を溜め込むので,必要に応じて閉じる.
要所要所で必要なだけならIteratorを使う
for式 -> loopのための構文じゃない!!
Optionがふたつあって両方ともSomeな時に何かしたい.
def oppai(o1: Option[String], o2: Option[Int], o3: Option[Int]) = o1 flatMap { v1 => o2 flatMap{ v2 => o3 map { v1 * v2 * v3 } } } foreach { println } }
=>ネストが深くなるので,forで対応
def oppai(o1: Option[String], o2: Option[Int], o3: Option[Int]) = for( v1 <- o1; v2 <- o2; v3 <- o3 ) { println(v1 * v2 * v3) }
forには色々渡せる.map/flatMapが定義されているものなら何でも良い
OptionとかListとかFuture(actor !? msg)とか…
Partial Function
= 特定の引数にのみ結果が定義されている関数(定義域のようなもの)
val pf:PartialFunction[Int, String] = { case n if n > 0 => "hoge" * n }
- 1など入れると例外を投げる(MatchError)
pf.isDefinedAt(-1) -> false
例外投げるとか怖いし,ParialFunctionに定義されたliftを用いてOptionを返すようにする.
val lifted = pf.lift
lifted(2) = Some(hogehoge)
lifted(-1) = None
さらに,orElseでpf同士を合成出来る.
val pfElse = pf1 orElse pf2
pr1で定義されていなければ,pf2を呼び出してくれる.
- >Liftで用いられている?
Self-Type Annotation
traitがmixinされる先の型の制約を指定する
trait ActorExt{ self: Actor => // Actorに対してのみ拡張される事を指定 def !!!(msg:Any, times:Int = 2) = { for(_ <- 1 to times) this ! msg } }
- Structual Subtypingを利用したLoan pattern
// type = 部分型 type Resource = { read(): Int close(): Unit } trait ClosableResource{ // このtraitはreadとcloseを持っている型にしかmixin出来ない self: Resource => def open(f: Resource => Unit) = try{ f(this)} finally{ close() } }
Phantom Types -> 状態を付与して,コンパイラに設計の意図をチェックさせる
型パラメータを利用してメタデータを付与するデザインパターン
例:SQL文がきちんとエスケープされているかを確かめる
メタデータとしての型(実装は無い,ただのマーカー)
sealed trait Escaped trait Unsafe extends Escaped trait Safe extends Escaped Object Parameter{ def create(value: String) = Parameter[unsafe](value) // 初期値はUnsafeになる. } case class Parameter[A <: Escaped] // AにはSafe,Unsafeのいずれかが入る private (value: String)(implicit m:Manifest[A]){ def escape: Parameter[Safe] = if(m.erasure == classOf[Unsafe]) Parammeter[Safe](value) // escapeが呼ばれたらSafeになる }
ここでSQLを実行するメソッドでは引数にParameter[Safe]をとる
def executeQuery(params: Parameter[Safe]*) = … // Unsafeなモノは入らない
Manifest
Type erasure // java -> 型パラメータの型情報は実行時には失われている
例
def is[A](a:Any): Boolean = a.isInstanceOf[A]
is[String](1) -> true
実行時にもパラメータを判定させたい
def is[A](a:Any)(implicit m:Manifest[A]): Boolean = m.erasure.isInstance(a)
is[String](1) -> false
実際実行時にチェックさせるのってどうなの...
というわけで
Generalized Type Constraintsを使う
コンパイル時にチェックさせる!
Parameter[A]がUnsafeの場合にのみescapeが呼ばれるようにチェックしたい
A =:= Unsafe を用いる!!!
def escape(implicit ev: A =:= Unsafe) =
Parameter[Safe](value)
条件
A =:= B AとBは同じ型でなければならない
A
embed REPL
REPLはアプリケーションに組み込む事が出来る
ILoop, IMain,…