Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

プログラミング言語Scala

949 views

Published on

社内勉強会で発表したScalaの入門的内容です

Published in: Engineering
  • Be the first to comment

プログラミング言語Scala

  1. 1. プログラミング言語 Scala 安田裕介
  2. 2. 歴史 2001: スイス連邦工科大学のMartin Odersky教授がScalaの開発 に着手 2003: Javaプラットフォーム上でリリース 2006: Scala 2.0をリリース 2011: ヨーロッパ研究議会から230万€の研究助成金を獲得 OderskyらがScalaの商用サポートを行うTypesafe社を設立 Typesafe社がGreylock Partnersから300万$の投資を獲得
  3. 3. Scalaの特徴 • あらゆる規模に対応するスケーラブルな言語 • オブジェクト指向言語 • 関数型言語 • 静的型付け言語 多彩な表現力をもつマルチパラダイム言語だ
  4. 4. Scalaの特徴 そして何よりも強調したいのが、 プログラマーとともに成長する言語 プログラマーを成長させる言語
  5. 5. ScalaはREPLをもっている • この発表で出てくる コードはREPLで確認 可能 • シェルを立ち上げて scalaコマンドをたた こう
  6. 6. 1.Scalaは オブジェクト指向言語 • Scalaは徹底した純粋オブジェクト指向言語 • すべてのメソッド、フィールドはオブジェクトに属 している • あらゆる型の値はオブジェクトである
  7. 7. ScalaのHello World! println("Hello World!") ScalaのHello World!は非常に短い • セミコロン推論 • printlnはPredefオブジェクトのメンバー • Predefオブジェクトはあらかじめimportされる • Javaのように静的mainメソッドをもつクラスを作る必要はない
  8. 8. Scalaではすべてのメソッド、 フィールドはオブジェクトに属する • Scalaはクラスがメソッドやフィールドなどのメンバーをもつことを認め ていない。 • Scalaではメソッドやフィールドはすべてオブジェクトに属する • Scalaではオブジェクトが主役となる • objectキーワードでシングルトンオブジェクトをダイレクトに生成できる object Singleton { val zero = 0 def sum(x: Int, y: Int) = x + y }
  9. 9. あらゆる型の値は オブジェクトである Scalaではすべての値がオブジェクトだ プリミティブ型はないvoidはない。nullはあるがあまり使わない。 scala> 1.toString res0: String = 1 ! scala> 1.0.toString res1: String = 1.0 ! scala> 'c'.toString res2: String = c ! scala> true.toString res3: String = true ! scala> "string".toString res4: String = string scala> val unit = (() => {})() unit: Unit = () ! scala> val map = Map(1 -> "i", 2 -> "ii") map: scala.collection.immutable .Map[Int,String] = Map(1 -> i, 2 -> ii) ! scala> val three = map.get(3) three: Option[String] = None すべてはAnyのサブクラスの値として表現される voidはUnit型の()オブジェクトに相当。 NullクラスのnullオブジェクトよりもOption[Nothing] 型のNoneオブジェクトの方をよく使う。
  10. 10. 演算子は存在しない すべてはメソッドだ • 演算子は存在しない。あるのはメソッドのみ。 • メソッド呼び出し形式と演算子形式を選択可能 • あらゆる特殊記号を識別子として使用可能 1 + 2 1.+(2) ←この2つの式は等価 REPLでTabを打ってみよう scala> 1. % + > >>> isInstanceOf toDouble toLong unary_+ | & - >= ^ toByte toFloat toShort unary_- * / >> asInstanceOf toChar toInt toString unary_~ scala> val l = List(1) l: List[Int] = List(1) scala> l. ++ /: :+ +: ++: :: ::: :
  11. 11. トレイト トレイトとは?:機能の集合で、実装をもったインターフェースと思ってよい class Point(_x: Int = 0, _y: Int = 0) { def x = _x def y = _y def +(target: Point) = new Point(x + target.x, y + target.y) override def toString = s"(${x}, ${y})" } trait Landscape extends Point { override def x = super.y override def y = super.x } trait Retina extends Point { val ratio = 2 override def x = super.x*ratio override def y = super.y*ratio def toNormal = new Point(super.x, super.y) } ! class LRPoint extends Landscape with Retina new Point(3, 5) with Landscape with Retina //res0: Point with Landscape with Retina = (10, 6) 実装を持っているので、インス タンス化時に直接ミックスイン することも可能 クラスは複数のトレイトをミックスインできる
  12. 12. 2.Scalaは関数型言語 関数型言語は以下の2つの要素をもつ言語だ • イミュータビリティ(不変性) • 第一級オブジェクトとしての関数 イミュータビリティはプログラムに安全性、拡張性をもたらし 関数オブジェクトは柔軟性、簡潔性、表現力をもたらす
  13. 13. イミュータビリティ (不変性) • 変更不可能な変数宣言 • イミュータブルなデータ型 • パターンマッチ
  14. 14. 変更不可能な変数宣言 再代入不可能な変数宣言にはvalを使う scala> val abc = "abc" abc: String = abc ! scala> abc = "cde" <console>: error: reassignment to val abc = "cde" ^ 再代入可能な変数宣言にはvarを使う scala> var abc = "abc" abc: String = abc ! scala> abc = "cde" abc: String = cde
  15. 15. イミュータブルなデータ型 Scalaでは不変なデータ型と可変なデータ型は区別されている。データ型はデフォルトで不変だ。 不変なデータ型ではあらゆる操作を何回施しても値はもとのままである。 scala> val m12 = Map("1" -> 1, "2" -> 2) m12: scala.collection.immutable.Map[String,Int] = Map(1 -> 1, 2 -> 2) scala> val m123 = m12 + ("3" -> 3) m123: scala.collection.immutable.Map[String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3) scala> m123 res0: scala.collection.immutable.Map[String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3) scala> m12 == m123 res1: Boolean = false Scalaは可変なデータ型を使う余地も残している scala> import scala.collection.mutable.Map import scala.collection.mutable.Map scala> val m12 = Map("1" -> 1, "2" -> 2) m12: scala.collection.mutable.Map[String,Int] = Map(2 -> 2, 1 -> 1) scala> val m123 = m12 += ("3" -> 3) m123: m12.type = Map(2 -> 2, 1 -> 1, 3 -> 3) scala> m12 res2: scala.collection.mutable.Map[String,Int] = Map(2 -> 2, 1 -> 1, 3 -> 3) scala> m12 == m123 res3: Boolean = true
  16. 16. パターンマッチ パターンマッチはパターンによって値と変数を束縛する。 パターンマッチを使えば代入などの副作用やキャスト、nullかどうかなどの判定 といった危険な処理を一切行わずにすむ。 例)翼を含めた動物の足の数を合計する abstract class Animal { val legs: Int } class Dog extends Animal { val legs = 4 } class Bird extends Animal { val legs = 2 val wings = 2 } val animals = List(new Dog, new Bird) //animals: List[Animal] = List(Dog@1e339ce8, Bird@4e8252d5) animals.map({ case bird: Bird => bird.legs + bird.wings case animal: Animal => animal.legs }).reduce(_ + _) //res0: Int = 8
  17. 17. 第一級オブジェクトとしての関数 第一級オブジェクトとは オブジェクトがもつべきあらゆる能力をもつオブジェクト •変数に代入することができる scala> val sum = (x: Int, y: Int) => x + y sum: (Int, Int) => Int = <function2> •関数の引数に渡すことができる scala> List(1,2,3).map(_ * 2) res0: List[Int] = List(2, 4, 6) •関数の返り値として返すことができる scala> def optioned[A, B](f: A => B) : Option[A] => Option[B] = _ map f optioned: [A, B](f: A => B)Option[A] => Option[B] •メンバーをもつ scala> ((x: Int) => x + 1).compose((x: Int) => x * 2) res1: Int => Int = <function1>
  18. 18. 関数適用ができるオブジェクト オブジェクトは呼び出すものではないが、Scalaでは引数を適用で きる scala> :paste Entering paste mode (ctrl-D to finish) class Applicable(val s: String) ! object Applicable { def apply(s: String) = new Applicable(s) } Exiting paste mode, now interpreting. defined class Applicable defined object Applicable ! scala> Applicable("abc") res0: Applicable = Applicable@1cb285b コンパイラはオブジェクトへの引数の適用をapplyメソッド呼び出しに置き換える。 ListやMapに引数を適用できるのは、それらがapplyメソッドを定義しているからだ。
  19. 19. 値を返す制御構造 if, for, whileのような制御構造はすべて式であり、値を返す。 val result = if ("string" == new String("string")) “reasonable" else "unreasonable" result: String = reasonable for式はコンプリヘンション形式でフィルターをかけられる。 yieldキーワードでコレクションを作ることができる。 scala> for (i <- 1 until 9 if i % 2 == 0) yield i res0: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8)
  20. 20. match式 switchに相当。パターンマッチにより値を抽出し、それを返す。 abstract class Expr case class Var(name: String) extends Expr case class Number(num: Double) extends Expr case class UnOp(operator: String, arg: Expr) extends Expr case class BinOp(operator: String, left: Expr, right: Expr) extends Expr ! val twoPlusZero = BinOp("+", Number(2), Number(0)) ! def simplify(expr: Expr) = { expr match { case UnOp("-", UnOp("-", e)) => e case BinOp("+", e, Number(0)) => e case BinOp("*", e, Number(1)) => e case _ => expr } } ! val number = simplify(twoPlusZero) number: Expr = Number(2.0) ※コップ本に載っている例
  21. 21. caseクラス クラスとすべての引数の情報を再帰的に木構造にして内部に保 持することで、再帰的なパターンマッチを可能にするクラス caseクラスは以下を自動定義する 1.objectのapplyメソッドを定義val v = Var("x") val twoPlusZero = BinOp("+", Number(2), Number(0)) scala> twoPlusZero.toString res1: String = BinOp(+,Number(2.0),Number(0.0)) twoPlusZero.hashCode res2: Int = -571098259 BinOp("+", Number(2), Number(0)) == BinOp("+", Number(2), Number(0)) res3: Boolean = true v.name //res0: String = x 2.コンストラクタ引数にvalプレフィックスを付与 3.toString, hashCode, equalsメソッドを定義
  22. 22. カリー化 複数のパラメーターリストをもつ関数を定義できる カリー化によって関数を部分的に適用できる scala> def twice[T](arg: T)(op: (T) => T) = op(op(arg)) ! twice(3)(x => x * x) res0: Int = 81 ! scala> val three = twice(3) _ three: (Int => Int) => Int = <function1> ! scala> three(x => x * x) res1: Int = 81 twice(3) {x => val square = x * x square + 1 } res2: Int = 101 ()は{}に置き換え可能。 このように書くとまるで独自の 制御構造を作っているようだ
  23. 23. 暗黙の型変換 AndroidのsetOnClickListenerにFunctionオブジェクトを渡したい class MainActivity extends TypedActivity { override def onCreate(bundle: Bundle) { super.onCreate(bundle) setContentView(R.layout.main) val view = findView(TR.button) view.setOnClickListener(new View.OnClickListener { override def onClick(v: View) = Log.d("pressed", "button") }) } } ※android-sdk-pluginテンプレートを使用 暗黙の型変換を定義すれば関数オブジェクトを渡すことができる implicit def func2OnClick(f: () => _) = new OnClickListener { override def onClick(v: View) = f } ! view.setOnClickListener(() => Log.d("pressed", "button"))
  24. 24. 暗黙のパラメーター パラメーターリストが足りないまま関数が呼び出されたとき、足りな いパラメーターリストがimplicit宣言されていた場合、スコープ中の implicit宣言された値で関数を完成させる def maxList[T](elements: List[T])(implicit orderer: T => Ordered[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list") case List(x) => x case x :: rest => val maxRest = maxList(rest)(orderer) if (orderer(x) > maxRest) x else maxRest } ! scala> maxList(List(1,2,3,4,5)) res0: Int = 5 ※コップ本に載っている例 ScalaライブラリはT=>Ordered[T]の暗黙の定義を提供しているので、 Orderedトレイトを継承していないIntでもmaxListは使える
  25. 25. 3.Scalaは 静的型付け言語 Scalaは厳格な型付けを行うが、開発者を難しい型シ ステムから解放するいくつかの仕組みをもっている • 型推論 • ダックタイピング • ジェネリクスにおける変位指定のチェック
  26. 26. ダックタイピング ダックタイピングとは? アヒルのように鳴けばそれはアヒルであるという考えのもと、メンバー のシグニチャが同じであれば型が違っても互換性のある型とみなすこと def using[T <: {def close(): Unit}, S](obj: T)(operation: T => S) = { val result = operation(obj) obj.close() result } ! import java.io.PrintWriter ! using(new PrintWriter("date.txt")) { writer => writer.println(new java.util.Date) } ※コップ本に載っている例 ここではPrintWriterを渡しているが、セッションやソケットなど、close メソッドを持っているオブジェクトなら何でも渡せる
  27. 27. 変位指定のチェック ジェネリックな型には変位という概念がある 変位の種類は3つある。 Sのサブ型がTであるとき 非変:Class[T]とClass[S]にサブ型関係はない 共変:Class[T]はClass[S]のサブ型である 反変:Class[T]はClass[S]のスーパー型である ここで共変な型Cell[+T]を定義する。しかしコンパイルを通らない。なぜだろうか? class Cell[+T](init: T) { private[this] var current = init def get = current def set(x: T) { current = x } } error: covariant type T occurs in contravariant position in type T of value x def set(x: T) { current = x } ^ ※コップ本に載っている例
  28. 28. 変位指定のチェック さきほどのコードがコンパイルを通らないのは、以下のよ うな反例が書けるからである val c1 = new Cell[String]("abc") val c2: Cell[Any] = c1 c2.set(1) val s: String = c1.get このコードは型に矛盾はない。しかし結果としてString型 の変数にInt型の値を代入することを許している。もしこれ がコンパイルを通ると、実行時エラーの危険を生む。 Scalaコンパイラは型パラメーターが現れるすべての場所を チェックし、危険があれば開発者に教えてくれる
  29. 29. さらにその先へ Scalaコレクション アクター アドホック多相性モナド 並列コレクション パーサー・コンビネーター抽出子
  30. 30. まとめ • Scalaは非常に表現力が高く、言語そのものを拡張 する感じで書ける • Scalaは様々なパラダイムを取り込んでおり、それ らを徐々に習得することで開発者は成長できる いつやるの?

×