Scalaのオブジェクトの話

  • 3,316 views
Uploaded on

Scalaの関数型プログラミングの中で、オブジェクトがどのような位置づけで使用されるかの説明した資料です。object, class, traitがMLのモジュールシステムとどのように対応するかを中心に解説しています。

Scalaの関数型プログラミングの中で、オブジェクトがどのような位置づけで使用されるかの説明した資料です。object, class, traitがMLのモジュールシステムとどのように対応するかを中心に解説しています。

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
3,316
On Slideshare
0
From Embeds
0
Number of Embeds
3

Actions

Shares
Downloads
17
Comments
0
Likes
14

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 2014.6.25    第8回  Functional  忍者 ⼊入⼀一  前⽥田康⾏行行  (@maeda_̲) Scalaの オブジェクト の話
  • 2. ⾃自⼰己紹介 前⽥田康⾏行行  ( twitter : @maeda_ ) 名古屋在住  フリーランス 屋号:⼊入⼀一 ( http://www.illi-ichi.com )
  • 3. はじめに •  Scalaはオブジェクト指向⾔言語 –  既存資産を活かせるし、既存プログラマも分かりやすい –  強⼒力力なモジュールシステム 今回のテーマ Scalaの関数型プログラミングの中で オブジェクトをどのように使うのか?
  • 4. 注意 •  この資料料では –  Scalaでは関数とメソッドは少し違うけど、区別せずに 関数という –  関数型⾔言語  = 静的型付け関数型⾔言語
  • 5. Scalaでの オブジェクトの⽴立立ち位置
  • 6. 関数型⾔言語との対応 Scala は 1. 関数型⾔言語のパーツをJavaのパーツにマッピングして 2. 使いやすいようにシンタックスなどを整えた⾔言語 パーツ ML Scala 関数 関数 メソッド 関数オブジェクト 代数データ型 ヴァリアント レコード case class case object モジュールシステム モジュール ファンクタ object class ※ Haskellは型クラスによりMLのモジュールシステムと同等の機能を実現可能 (参考)    ML Modules and Haskell Type Classes: A Constructive Comparison http://www.cse.unsw.edu.au/~chak/papers/modules-classes.pdf
  • 7. 関数型⾔言語との対応 Scala は 1. 関数型⾔言語のパーツをJavaのパーツにマッピングして 2. 使いやすいようにシンタックスなどを整えた⾔言語 パーツ ML Scala 関数 関数 メソッド 関数オブジェクト 代数データ型 ヴァリアント レコード case class case object モジュールシステム モジュール ファンクタ object class ※ Haskellは型クラスによりMLのモジュールシステムと同等の機能を実現可能 (参考)    ML Modules and Haskell Type Classes: A Constructive Comparison http://www.cse.unsw.edu.au/~chak/papers/modules-classes.pdf
  • 8. モジュールとは この資料料において、モジュールとは ある機能についての型、値 / 関数 をまとめたもの
  • 9. オブジェクトを作る •  objectキーワードにより シングルトンオブジェクト  (= モジュール) が作れる •  内部的には下記のようなイメージ object  Hoge  {      def  foo(...)  =  ...   }     Hoge.foo(...)              //  Hogeというモジュールのfooという関数を呼び出し   class  HogeClass(){      def  foo(...)  =  ...   }   val  Hoge  =  new  HogeClass()  
  • 10. クラスとは? •  パラメータ付きのモジュール •  パラメータを渡してインスタンス化 → モジュールができる class  Hoge(connectionString:  String){      def  foo(...)  =  ...   }     //  パラメータを渡して、モジュールの⽣生成   val  hoge  =  new  Hoge("jdbc:h2:mem:test")   hoge.foo(...)  
  • 11. オブジェクトがモジュールなので •  変数に代⼊入したり、関数に渡したり •  package名と同様にimportもできる class  Hoge(fuga:  Fuga){      import  fuga._    ...   }   Scalaのimportはどこでも書けるし、 スコープもある
  • 12. コンパニオンオブジェクト •  クラス名と同名のシングルトンオブジェクトは、 そのクラスのコンパニオンオブジェクトとなる。 •  コンパニオンオブジェクトにはそのクラスのヘルパー 関数を置く(Javaでいうstaticメソッド的なもの) •  コンストラクタにはクラス内で使うパラメータを渡す。 (下記のコード参照) //  privateをつけるとコンパニオンオブジェクトからしか⽣生成できない   class  Hoge  private  (...){  ...  }     object  Hoge  {      //  引数を加⼯工して、Hogeのインスタンスを⽣生成する関数      def  fromString(str:  String):  Hoge  =  new  Hoge(...)      def  fromFile(file:  File):  Hoge  =  new  Hoge(...)   }   ※ コンパニオンオブジェクトは対応するクラスと同じファイル内に記述すること ※ REPL上で試す場合は:pasteを使って⼊入⼒力力する
  • 13. 具体例例を⾒見見る
  • 14. お題 •  ⽂文字列列とそれに対応する動作を定義したマップが ある •  ⽂文字列列をパースして、マップのキーのうち、どれ をどこまで⼊入⼒力力したかを返す関数を作る val  menu:  Map[String,  Action]  =  Map(          "hungry"  -­‐>  GoTo("restaurant"),          "tired"  -­‐>  Sleep,          ...   )     Parser.parse("")              //  →  NoInput   Parser.parse("hun")        //  →  Entering(GoTo("restaurant"),  3)   Parser.parse("hunt")      //  →  WrongInput   Parser.parse("hungry")  //  →  Completed(GoTo("restaurant"))  
  • 15.     object  Parser  {      sealed  abstract  class  Action      case  class    GoTo(place:  String)  extends  Action      case  object  Sleep                              extends  Action        sealed  abstract  class  State      case  object  NoInput                                                                      extends  State      case  class  Entering(action:  Action,  count:  Int)              extends  State      case  class  Completed(action:  Action)                                    extends  State      case  object  WrongInput                                                                extends  State        val  menu:  Map[String,  Action]  =            Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...)        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     Parser.parse("hun")    //  →  Parser.Entering(Parser.GoTo("restaurant"),  3)   Parser.parse("hunt")  //  →  Parser.WrongInput   #1 objectでモジュールを作る
  • 16.     object  Parser  {      sealed  abstract  class  Action      case  class    GoTo(place:  String)  extends  Action      case  object  Sleep                              extends  Action        sealed  abstract  class  State      case  object  NoInput                                                                      extends  State      case  class  Entering(action:  Action,  count:  Int)              extends  State      case  class  Completed(action:  Action)                                    extends  State      case  object  WrongInput                                                                extends  State        val  menu:  Map[String,  Action]  =            Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...)        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     Parser.parse("hun")    //  →  Parser.Entering(Parser.GoTo("restaurant"),  3)   Parser.parse("hunt")  //  →  Parser.WrongInput   Parser モジュールの関数を呼び出す Parser モジュールを作って #1 objectでモジュールを作る
  • 17.     object  Parser  {      sealed  abstract  class  Action      case  class    GoTo(place:  String)  extends  Action      case  object  Sleep                              extends  Action        sealed  abstract  class  State      case  object  NoInput                                                                      extends  State      case  class  Entering(action:  Action,  count:  Int)              extends  State      case  class  Completed(action:  Action)                                    extends  State      case  object  WrongInput                                                                extends  State        val  menu:  Map[String,  Action]  =            Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...)        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     Parser.parse("hun")    //  →  Parser.Entering(Parser.GoTo("restaurant"),  3)   Parser.parse("hunt")  //  →  Parser.WrongInput   代数データ型の定義 #1 objectでモジュールを作る
  • 18.     object  Parser  {      sealed  abstract  class  Action      case  class    GoTo(place:  String)  extends  Action      case  object  Sleep                              extends  Action        sealed  abstract  class  State      case  object  NoInput                                                                      extends  State      case  class  Entering(action:  Action,  count:  Int)              extends  State      case  class  Completed(action:  Action)                                    extends  State      case  object  WrongInput                                                                extends  State        val  menu:  Map[String,  Action]  =            Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...)        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     Parser.parse("hun")    //  →  Parser.Entering(Parser.GoTo("restaurant"),  3)   Parser.parse("hunt")  //  →  Parser.WrongInput   ⽂文字列列と動作の対応を 記述するマップ #1 objectでモジュールを作る 関数の定義
  • 19.     object  Parser  {      sealed  abstract  class  Action      case  class    GoTo(place:  String)  extends  Action      case  object  Sleep                              extends  Action        sealed  abstract  class  State      case  object  NoInput                                                                      extends  State      case  class  Entering(action:  Action,  count:  Int)              extends  State      case  class  Completed(action:  Action)                                    extends  State      case  object  WrongInput                                                                extends  State        val  menu:  Map[String,  Action]  =            Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...)        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     Parser.parse("hun")    //  →  Parser.Entering(Parser.GoTo("restaurant"),  3)   Parser.parse("hunt")  //  →  Parser.WrongInput   #1 objectでモジュールを作る メニューを外部から 注⼊入したい
  • 20.       sealed  abstract  class  Action   case  class    GoTo(place:  String)  extends  Action   case  object  Sleep  extends  Action     class  Parser(menu:  Map[String,  Action])  {      sealed  abstract  class  State      case  object  NoInput                                                                      extends  State      case  class  Entering(action:  Action,  count:  Int)              extends  State      case  class  Completed(action:  Action)                                    extends  State      case  object  WrongInput                                                                extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     val  parser  =  new  Parser(      Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...))     parser.parse("hun")        //  →  parser.Entering(GoTo("restaurant"),  3)   parser.parse("hunt")      //  →  parser.WrongInput     #2 メニューをパラメータとして渡す
  • 21.       sealed  abstract  class  Action   case  class    GoTo(place:  String)  extends  Action   case  object  Sleep  extends  Action     class  Parser(menu:  Map[String,  Action])  {      sealed  abstract  class  State      case  object  NoInput                                                                      extends  State      case  class  Entering(action:  Action,  count:  Int)              extends  State      case  class  Completed(action:  Action)                                    extends  State      case  object  WrongInput                                                                extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     val  parser  =  new  Parser(      Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...))     parser.parse("hun")        //  →  parser.Entering(GoTo("restaurant"),  3)   parser.parse("hunt")      //  →  parser.WrongInput     #2 メニューをパラメータとして渡す classにして外からメニュー を渡せるように インスタンス化して モジュールを⽣生成
  • 22.       sealed  abstract  class  Action   case  class    GoTo(place:  String)  extends  Action   case  object  Sleep  extends  Action     class  Parser(menu:  Map[String,  Action])  {      sealed  abstract  class  State      case  object  NoInput                                                                      extends  State      case  class  Entering(action:  Action,  count:  Int)              extends  State      case  class  Completed(action:  Action)                                    extends  State      case  object  WrongInput                                                                extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     val  parser  =  new  Parser(      Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...))     parser.parse("hun")        //  →  parser.Entering(GoTo("restaurant"),  3)   parser.parse("hunt")      //  →  parser.WrongInput     #2 メニューをパラメータとして渡す Action型の内容は Parserの処理理に依存しない これも外に出したい
  • 23.       sealed  abstract  class  Action   case  class    GoTo(place:  String)  extends  Action   case  object  Sleep                              extends  Action     class  Parser[A](menu:  Map[String,  A])  {      sealed  abstract  class  State      case  object  NoInput                                                      extends  State      case  class  Entering(action:  A,  count:  Int)        extends  State      case  class  Completed(action:  A)                              extends  State      case  object  WrongInput                                                extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     val  parser:  Parser[Action]  =  new  Parser(      Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...))     parser.parse("hun")        //  →  parser.Entering(GoTo("restaurant"),  3)   parser.parse("hunt")      //  →  parser.WrongInput   #3 型も外から指定するように
  • 24.       sealed  abstract  class  Action   case  class    GoTo(place:  String)  extends  Action   case  object  Sleep                              extends  Action     class  Parser[A](menu:  Map[String,  A])  {      sealed  abstract  class  State      case  object  NoInput                                                      extends  State      case  class  Entering(action:  A,  count:  Int)        extends  State      case  class  Completed(action:  A)                              extends  State      case  object  WrongInput                                                extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     val  parser:  Parser[Action]  =  new  Parser(      Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...))     parser.parse("hun")        //  →  parser.Entering(GoTo("restaurant"),  3)   parser.parse("hunt")      //  →  parser.WrongInput   #3 型も外から指定するように Parserモジュールは任意の型 で受け取れる 型パラメータがついた
  • 25. abstract  class  Parser  {      type  A      def  menu:  Map[String,  A]        sealed  abstract  class  State      case  object  NoInput                                                      extends  State      case  class  Entering(action:  A,  count:  Int)        extends  State      case  class  Completed(action:  A)                              extends  State      case  object  WrongInput                                                extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     sealed  abstract  class  Action   case  class    GoTo(place:  String)              extends  Action   case  object  Sleep                                          extends  Action     val  parser:  Parser  =  new  Parser{      type  A  =  Action      val  menu  =  Map("hungry"  -­‐>  GoTo("restaurant"),                                      "tired"  -­‐>  Sleep,  ...  )   }     parser.parse("hun")        //  →  parser.Entering(GoTo(Restaurant),  3)   parser.parse("hunt")      //  →  parser.WrongInput   #4  型パラメータを抽象型で      書き換えてみる
  • 26. abstract  class  Parser  {      type  A      def  menu:  Map[String,  A]        sealed  abstract  class  State      case  object  NoInput                                                      extends  State      case  class  Entering(action:  A,  count:  Int)        extends  State      case  class  Completed(action:  A)                              extends  State      case  object  WrongInput                                                extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     sealed  abstract  class  Action   case  class    GoTo(place:  String)              extends  Action   case  object  Sleep                                          extends  Action     val  parser:  Parser  =  new  Parser{      type  A  =  Action      val  menu  =  Map("hungry"  -­‐>  GoTo("restaurant"),                                      "tired"  -­‐>  Sleep,  ...  )   }     parser.parse("hun")        //  →  parser.Entering(GoTo(Restaurant),  3)   parser.parse("hunt")      //  →  parser.WrongInput   ここで型を指定 これが抽象型 具体的な型は⼦子クラスで #4  型パラメータを抽象型で      書き換えてみる
  • 27. abstract  class  Parser  {      type  A      def  menu:  Map[String,  A]        sealed  abstract  class  State      case  object  NoInput                                                      extends  State      case  class  Entering(action:  A,  count:  Int)        extends  State      case  class  Completed(action:  A)                              extends  State      case  object  WrongInput                                                extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     sealed  abstract  class  Action   case  class    GoTo(place:  String)              extends  Action   case  object  Sleep                                          extends  Action     val  parser:  Parser  =  new  Parser{      type  A  =  Action      val  menu  =  Map("hungry"  -­‐>  GoTo("restaurant"),                                      "tired"  -­‐>  Sleep,  ...  )   }     parser.parse("hun")        //  →  parser.Entering(GoTo(Restaurant),  3)   parser.parse("hunt")      //  →  parser.WrongInput   型パラメータが消えた #4  型パラメータを抽象型で      書き換えてみる
  • 28. abstract  class  Parser  {      type  Action      def  menu:  Map[String,  Action]        sealed  abstract  class  State      case  object  NoInput                                                            extends  State      case  class  Entering(action:  Action,  count:  Int)    extends  State      case  class  Completed(action:  Action)                          extends  State      case  object  WrongInput                                                      extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }       val  parser:  Parser  =  new  Parser{      sealed  abstract  class  Action      case  class    GoTo(place:  String)              extends  Action      case  object  Sleep                                          extends  Action        val  menu  =  Map("hungry"  -­‐>  GoTo("restaurant"),                                      "tired"  -­‐>  Sleep,  ...  )   }     parser.parse("hun")        //  →  parser.Entering(GoTo(Restaurant),  3)   parser.parse("hunt")      //  →  parser.WrongInput   #5  Action型の定義を      ⼦子クラスに移す
  • 29. abstract  class  Parser  {      type  Action      def  menu:  Map[String,  Action]        sealed  abstract  class  State      case  object  NoInput                                                            extends  State      case  class  Entering(action:  Action,  count:  Int)    extends  State      case  class  Completed(action:  Action)                          extends  State      case  object  WrongInput                                                      extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }       val  parser:  Parser  =  new  Parser{      sealed  abstract  class  Action      case  class    GoTo(place:  String)              extends  Action      case  object  Sleep                                          extends  Action        val  menu  =  Map("hungry"  -­‐>  GoTo("restaurant"),                                      "tired"  -­‐>  Sleep,  ...  )   }     parser.parse("hun")        //  →  parser.Entering(GoTo(Restaurant),  3)   parser.parse("hunt")      //  →  parser.WrongInput   #5  Action型の定義を      ⼦子クラスに移す ここで抽象型の具体型を定義
  • 30. abstract  class  Parser  {      type  Action      def  menu:  Map[String,  Action]        sealed  abstract  class  State      case  object  NoInput                                                                extends  State      case  class  Entering(action:  Action,  count:  Int)        extends  State      case  class  Completed(action:  Action)                              extends  State      case  object  WrongInput                                                          extends  State        def  parse(String  input):  State  =  ...      def  maxMenuLength:  Int  =  ...   }     object  SomeParser  extends  Parser{      sealed  abstract  class  Action      case  class    GoTo(place:  String)              extends  Action      case  object  Sleep                                          extends  Action        val  menu  =  Map("hungry"  -­‐>  GoTo("restaurant"),  "tired"  -­‐>  Sleep,  ...)   }     SomeParser.parse("hun")  //  →  SomeParser.Entering(GoTo("restaurant"),  3)   SomeParser.parse("hunt")//  →  SomeParser.WrongInput   #6 objectで定義する
  • 31. パス依存型 •  別のモジュールに属する型は、別の型として扱われな いと不不⾃自然 •  コンパイラは型が属するオブジェクトを辿って、型が ⼀一致するかを判断する。これをパス依存型という。 •  パス依存型により、モジュールとして素直に扱える。 (JavaのInner Classはパス依存型ではない) val  someParser  =  new  Parser(...)   val  antotherParser  =  new  Parser(...)     someParser.parse(...)          //  →  someParser.During(...)   anotherParser.parse(...)    //  →  anotherParser.During(...)     //  以下はコンパイルエラー   val  state:  someParser.State  =  anotherParser.parse(...)  
  • 32. Scalaのモジュールは簡単 (だったよね?) •  オブジェクトをモジュールとして扱うのは、感覚的に も素直に捉える事が出来る •  モジュールを導⼊入する事に躊躇せず、気軽に使える •  ScalaのクラスはMLではファンクタが対応するが... ※「プログラミング in OCaml 五⼗十嵐嵐淳著」より抜粋 難しい印
  • 33. ⾖豆知識識:全称型と存在型 •  Scala特有の⽤用語ではなく、⼀一般的な⽤用語として。 すべての型Aについて、モジュール内の関数に型がつく。 これは全称量量化(∀)に対応し全称型という。 ある型Aについて、モジュール内の関数に型がつく。 これは存在量量化 (∃)に対応し存在型という。 abstract  class  Klass{      type  A      def  show:  A  =>  String   }     abstract  class  Klass[A]{      def  show:  A  =>  String   }     ※ Scalaのキーワードとして存在型(forSome)があるが、上記のように抽象型 により存在型は実現できるため、forSomeはオワコン
  • 34. クラスの機能を分解して考える
  • 35. クラスと関数の類似性 •  クラスはパラメータが渡せるというけど... •  パラメータを保持した関数が欲しいなら、 部分適⽤用した関数で実現できる //  クラスの場合   class  Parser[A](menu:  Map[String,  A]){      def  parse(input:  String)  :  State[A]  =  ...   }     //  関数の場合   def  parse[A](menu:  Map[String,  A])(input:  String):  State[A]  =  ...   val  parser:  String  =>  State[Action]  =  parse(Map(...))     ⼀一つ⽬目の引数だけ渡す。 残りの⼆二つ⽬目の引数を渡した時に 関数が評価される
  • 36. クラスとレコードの類似性 •  モジュールに2つ関数がある場合は 2つの名前付き関数の組を返す関数で実現できる •  これはMLのレコードに対応する //  クラスの場合   class  Parser[A](menu:  Map[String,  A]){      def  parse(input:  String)  :  State[A]  =  ...      def  maxMenuLength:  Int  =  ...   }     //  関数の場合   type  Parser[A]  =  {      def  parse(input:  String)  :  State[A]      def  maxMenuLength:  Int   }   def  createParser[A](menu:  Map[String,  A]):  Parser  =  ...    
  • 37. オブジェクトへの機能の集約 •  MLでは値(関数)、モジュール、レコード、ヴァ リアントはそれぞれが別々に分かれている ※ OCamlにはモジュールと値の相互変換をする機能があ るが、明⽰示的に変換して⾏行行き来する •  その観点で⾔言うと、 Scalaのオブジェクトやクラスはそれらが全部混 じってる •  さらに、Java⽬目線で考えると、クラスメソッドや パッケージもオブジェクトに集約されている
  • 38. 機能集約はよいのか? •  オブジェクトが何でもできること⾃自体は⾃自然 •  実際の感覚として、なんでもオブジェクトなので、適当に 書いてから、徐々に書き換えやすいと感じてる •  オブジェクトやクラスは簡単でお気軽で使いやすいと思う •  客観的な⻑⾧長所はまとめれず... (短所は、型推論論が弱いとか、代数データ型などのシンタッ クスがごちゃごちゃしてるとかはっきりしてるけど) 私にとって、本当のオブジェクトのセマンティクスに関する素晴らしいことの 1つは、本当のオブジェクトが「端から端まで本当のコンピュータ (RCATWD)」であることです。これによって、何でも表せる完全な能⼒力力をいつ でも保持できます。    ー  アラン・ケイ http://www.infoq.com/jp/news/2010/07/objects-smalltalk-erlang
  • 39. trait
  • 40. traitで分割 •  ひとつのモジュールが⼤大きくなってきたら、trait で分断できる object  Hoge  {                 }     trait  SomePart{         }     trait  AnotherPart{         }     //  traitを合成(mix-­‐in)してモジュールを作る   object  Hoge  extends  SomePart                            with  AnotherPart   Some Functions Another Functions Some Functions Another Functions
  • 41. 合成も簡単 •  インスタンス⽣生成時にもmix-inできる //  objectでモジュールを作る   object  Hoge  extends  SomePart  with  AnotherPart     //  インスタンス⽣生成時にmix-­‐in   val  hoge  =  new  SomePart  with  AnotherPart   val  fuga  =  new  SomePart  with  YetAnotherPart     //  条件によって、mix-­‐inされるものを切切り替える   val  foo  =  if  (config.availableXXX)  new  SomePart  with  XXX                        else  new  SomePart  with  Default      
  • 42. ⾃自分型 (self type) •  traitが他のtraitに依存することを⾃自分型により明 ⽰示する。 object  Hoge  {                 }     trait  SomePart{         }     trait  AnotherPart{  this  :  SomePart  =>         }     object  Hoge  extends  SomePart                            with  AnotherPart   Some Functions Another Functions Some Functions Another Functions Call ⾃自分型 mix-in時に⾃自分型を満たさないと コンパイルエラー SomePart内の 関数が使える
  • 43. traitによるシグネチャの記述 •  ⾃自分型として指定するtraitには具体的な実装は指 定しない⽅方がよい –  宣⾔言時には型シグネチャだけが欲しくて –  mix-inの時に実装が欲しい trait  HogeSignature  {                              //  シグネチャの宣⾔言      def  func(arg:  Int):  String   }     trait  Fuga  {  this:  HogeSignature  =>  //  使う側はシグネチャのみ必要      ...   }     trait  Hoge  extends  HogeSignature{      //  実装      def  func(arg:  Int):  String  =  ...   }     object  SomeModule  extends  Fuga  with  Hoge  //  合成    
  • 44. MLのsignatureとの対応 •  MLではモジュールの型が、そのモジュールの外部に対する インターフェースを⽰示す。 –  モジュールの実装はstructure(struct)を、 –  その型の指定はsignature(sig)  を使う。 •  実装のないtraitはMLのsignatureに対応する (*  OCamlのREPLで実⾏行行した場合  *) # module Three = struct let x = 3 end;;                                            ← 実装 module Three : sig val x : int end                                                          ← 型 # module type X_int = sig val x : int end;;                                        ← 型 module type X_int = sig val x : int end                                                  ← 型 # module Increment (M : X_int) = struct let x = M.x + 1 end;;      ← 実装 module Increment : functor (M : X_int) -> sig val x : int end                ← 型 ※「Real World Ocaml」Chapter 9 Functorより抜粋
  • 45. その他 各種ライブラリでのtraitの⽤用例例 •  DSLの語彙を増やす //  ScalaTestの例例                                                                        //  ShouldMatcherをmix-­‐inすると...   class  HogeSpec  extends  Specification  with  ShouldMatcher  {      //  中でshouldが使えるように      "hoge"  in  {            hoge  should  equal  (3)      }       //  Akka(Actorなどの並列列分散ライブラリ)の例例                                                                      //  ActorLoggingをmix-­‐inすると...   class  HogeActor  extends  Actor  with  ActorLogging  {        //  受け取ったメッセージがログに出⼒力力される        def  receive  =  {  ...     •  機能を付与する
  • 46. あってよかった オブジェクト指向のあれ
  • 47. あってよかった? •  Scalaは基本的に関数型プログラミング –  基本的に型と関数の定義 –  それらをまとめるモジュールにオブジェクトを使う •  関数型プログラミングと相容れない オブジェクト指向っぽい機能について、 使いどころを考える
  • 48. あってよかった① オブジェクトによる状態の管理理 •  例例えば、結果をキャッシュする場合 –  モジュールが状態を管理理する場合 –  pureに書くなら 状態を引き回すのが⾯面倒 → Stateモナドで → 型が変わって⾯面倒 val  hoge  =  new  Hoge(bufferSize  =  100)  //  最新100件を記憶     val  result1  =  hoge.heavyCalc(10)   val  result2  =  hoge.heavyCalc(10)   val  cache  =  Cache.empty(bufferSize  =  100)   val  (result1,  cache1)  =  Hoge.heavyCalc(10,  cache)   val  (result2,  cache2)  =  Hoge.heavyCalc(10,  cache1)  
  • 49. あってよかった② override •  例例えば、テストごとでスタブを微妙に変えたい •  他のoverrideの使い⽅方として、Stackable Traitという テクニックもある (詳細な説明) http://www.artima.com/scalazine/articles/stackable_trait_pattern.html "ほげのテスト"  in  {        val  stub  =  new  SomeStub{  override  def  hoge(...)  =  ...  }        ...   }   "ふがのテスト"  in  {        val  stub  =  new  SomeStub{  override  def  fuga(...)  =  ...  }        ...   }   trait  Hoge  with  Fuga{          abstract  override  def  foo(x:  X)  =  super.foo(modify(x))          ...  
  • 50. あってよかった③ オブジェクト指向っぽい構造 •  例例えば、case objectを列列挙型として使う時に、 それぞれのパラメータをつけたいとき •  ⼀一般的に –  オブジェクト指向は対象を追加しやすい –  関数型は操作を追加しやすい sealed  abstract  class  ErrorReason(val  errorCode:  String)   case  object  InvalidInput    extends  ErrorReason("E001")   case  object  NotAuthorized  extends  ErrorReason("E002")   ...  
  • 51. 結び •  オブジェクト指向の定義はないし、⾔言語によって、 オブジェクトの⽴立立ち位置は異異なる •  Scalaでは、オブジェクトはモジュールシステムの ためにあり、関数プログラミングと融合している •  関数型⾔言語的であるほど、優れた⾔言語であるわけ でなく、オブジェクト指向にもメリットもある •  Scalaならどちらのメリットも享受できる