Comments
Description
Transcript
新世代オブジェクト指向言語Scalaの魅力
新世代オブジェクト指向言語Scalaの魅力 Scala コミュニティ 水島 宏太 2011/12/01 0 自己紹介 ※所属会社の業務とScalaに関係はありません 氏名:水島宏太 株式会社イーフロー所属の1エンジニア Scala関連の記事・書籍執筆・勉強会発表等 余暇の時間で 刺激を求める技術者に捧げるScala講座(第6回・第17回担当、第23回・24回(執筆中)) 日経BP ITProの連載コーナー http://itpro.nikkeibp.co.jp/article/COLUMN/20080613/308019/ オライリージャパン 『プログラミングScala』 (レビュー) 秀和システム 『Scala実践プログラミング―オープンソース徹底活用』(共著) インプレス・ジャパン 『Scalaスケーラブルプログラミング第2版』 Scala 2.9解説記事執筆 プログラミングの魔道書 vol.2. 『プログラミング言語Scalaの歴史とこれから 』 寄稿 Scala Days 2010(スイス), Scala Days 2011(アメリカ) 参加・発表 その他多数 プログラミング言語・構文解析好き 自作のプログラミング言語Onion等をgithubで公開 1 Scalaの概要 Java VM上で動作するプログラミング言語 Javaの資産はほぼ完全に利用可能 Glue Code不要 静的型を持ったオブジェクト指向言語 クラス、オブジェクトと言ったJavaの概念はそのまま利用可能 高速な処理系 「Javaらしい」コードはJavaとほぼ同じ速度 「Scalaらしい」コードも十分に高速 型推論による冗長な記述の排除 静的型による安全性はそのままに 不変オブジェクトを中心とした安全で強力なコレクションライブラリ XMLリテラルの言語・標準ライブラリによるサポート 簡潔なXML処理 標準ライブラリでActorをサポート 明示的なロック不要の並行処理記述ライブラリ 標準ライブラリにパーザライブラリ同梱 カスタムDSLや設定ファイルのパーザを尐ない工数で書ける 2 Scala年表 2002: スイス連邦工科大学ローザンヌ校(EPFL)で開発開始 主要開発者:Martin Odersky javac (Java 5以降)オリジナル開発者 && Java Generics提案者の一人 2003: 2005: 2006: 2007: 2008: 最初の公開版リリース ~Scala 1.X Scala 2.0リリース~Scala 2.5リリース Scala 2.6リリース Scala 2.7リリース Java Genericsと互換に 2008: 2010: 2011: 2011: TwitterによるScalaの採用(バックエンド処理の置き換え) Scala 2.8リリース Scala 2.9リリース Typesafe社設立 CEO: Martin Odersky, CTO: Jonas Bonér(Scala製ミドルウェアAkkaの開発者) Scalaの商用サポート、コンサルティング、IDEプラグイン開発等 "Typesafe Stack"の提供 2011: Typesafe社、Play 2.0 Frameworkの商用サポート発表 3 Scala採用事例(海外) ※ 公開されている事例のみ紹介 Twitter LinkedIn Foursquare Amazon.com VMWare Novell Xerox NASA Bank of America UBS Remember the Milk Siemens GridGain OPOWER The Guardian その他多数 4 Scala採用事例(国内) ※ 公開されている事例のみ紹介 株式会社パテントビューロ Webサービス開発などの主力言語としてScalaを採用 知財判例データベース、astamuse(特許情報閲覧・検索サービス)等 有限会社ITプランニング Scalaによるシステム構築事例 GMOメディア株式会社 リワード広告システム等 株式会社ドワンゴ 導入予定 参考: ドワンゴ社内 scala勉強会(ニコニコ生放送) http://live.nicovideo.jp/watch/lv71111927 等 5 国内のScalaコミュニティ・勉強会 Scala勉強会 in 渋谷 (rpscala) 渋谷・秋葉原などで毎週勉強会を開催 Akasaka.scala 赤坂を中心に勉強会を開催 天領倉敷Scala 岡山付近のScalaコミュニティ 大阪Scala勉強会 Scala勉強会@東北 オンライン上の勉強会をほぼ毎週開催 東日本大震災の影響で休止中 名古屋Scala勉強会 名古屋を中心に活動 Scala@福岡 福岡を中心に活動 6 Hello, Scala object Hello { def main(args: Array[String]): Unit = { println("Hello, World!") } } Hello, Worldプログラム (Scala) class Hello { public static void main(String[] args){ System.out.println("Hello, World!") } } Hello, Worldプログラム (Java) さようなら, null. こんにちは, Option[T]. 「null参照の概念は10億ドル単位の過ち 」 by アントニー・ホーア Option型によって、「nullを取り得る型」を区別 // 「失敗する」可能性が型から判別できる val x: Option[String] = map.get("key") // val y: String = x // コンパイルエラー val y: String = x.getOrElse("default") Optionを使ったプログラム断片 (Scala) // nullを取りうるか型から判別できない String x = map.get("key"); // String y = x; // コンパイルできる String y = x == null ? x : "default"; nullを使ったプログラム片 (Java) お手軽型推論 自明な型をコンパイラが推論(型推論) 自明でない型(メソッドの引数等)は書く val map = new HashMap[String, List[String]]() // 型を書いても良い val map: Map[String, List[String]] = ... Map型の変数宣言(Scala) // 自明な型でも書かなければいけない Map<String, List<String>> map = new HashMap<String, List<String>>(); Map型の変数宣言 (Java) 無名クラスのシンタックスシュガー いわゆる「関数リテラル」と呼ばれているもの 実体は単なる無名クラスのシンタックスシュガー Java 8のラムダ式(予定)と異なり、変更可能な外部変数も参照可能 関数型プログラミングを知らなくても使える val multiply = (x: Int, y: Int) => x * y println(multiply(5, 10)) val multiply = new Function2[Int, Int] { def apply(x: Int, y: Int): Int = x * y } println(multiply.apply(5, 10)) ケースクラス(case classes) よくある「値オブジェクト」のクラス定義を簡潔に記述できる case class Person(name: String, age: Int) // ... val person = Person("hoge", 18) 値オブジェクトのクラス定義と生成(Scala) class Person { private final String name; private final int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public String getAge() { return age; } } //... new Person("hoge", 18) 値オブジェクトのクラス定義と生成(Java) Loanパターン (VS. Java 7 ARM(Automatic Resource Management)) (1) Loanパターン: リソースの自動closeを「ライブラリとして」提供 Java 7 ARM: リソースの自動closeを「構文として」提供 dispose()等でリソース破棄するライブラリには使えない // 一度「ライブラリとして」定義すれば def using[T <: Closeable](resource: T)(block: T => Unit): Unit = try { block(resource) } finally { resource.close() } // 組み込みの構文のように使える using(new FileInputStream("input.txt")){in => for(b <- Stream.continually(in.read()).takeWhile(_ != -1)) Console.out.write(b ^ 0xFF) } Loanパターン(Scala) try ( FileInputStream in = new FileInputStream("input.txt"); ){ int b; for (int b; (b = in.read()) != -1 ) { System.out.write(b ^ 0xFF); } } ARM(Java 7) Loanパターン (VS. Java 7 ARM(Automatic Resource Management)) (2) SWTでの比較: dispose()でリソースを解放 case class Wrapper[T <: { def dispose() }](core: T) extends Closeable{ def close() : Unit = core.dispose() } def wrap[T](core: T): Wrapper[T] = Wrapper(core) val display = new Display() using(wrap(new Shell(display))){ case Wrapper(shell) => /* ... */ shell.open() while (!shell.isDisposed()) if (!display.readAndDispatch()) display.sleep() } SWT + Loanパターン(Scala) Display display = new Display(); Shell shell = new Shell(display); /* ... */ try { shell.open(); while (!shell.isDisposed()) if (!display.readAndDispatch()) display.sleep(); } finally { shell.dispose(); // dispose()なので、ARM構文が使えない } SWT (Java 7) 強力なコレクションライブラリ (1) 簡潔な記述 より重要なロジックに集中できる val persons = List[Person](...) // 年齢が18歳以上の人の名前のリストを抽出 val namesAged18 = persons.collect{ case p if p.age >= 18 => p.name } //カンマで区切って出力 print(namesAged18.mkString(", ")) フィルタリング処理(Scala) List<Person> persons = new ArrayList<Person>(); /* ... */ List<String> namesAged18 = new ArrayList<String>(); for (Person p: persons) { if (p.getAge() >= 18) namesAged18.add(p.getName()); } if (namesAged18.size() > 0) { System.out.print(namesAged18.get(0)); namesAged18 = namesAged18.subList(1, namesAged18.size()); } for(String name: namesAged18) System.out.print(", " + name); フィルタリング処理 (Java) 強力なコレクションライブラリ (2) 不変と可変 「不変」コレクションと「可変」コレクションを別の型として管理 可読性の向上 + コレクションの取り扱いミスを防御 「型はドキュメント」の思想をJavaより進めている // Listは不変なので安全に引数として渡せる def concatWithLn1(names: List[String]): String = ... // ArrayBufferは可変なので、中で変更され得る事がわかる def concatWithLn2(names: ArrayBuffer[String]): String = ... リストの文字列を連結するメソッドのシグニチャ(Scala) // Listは可変 // (a) 防御的なコピー // (b) Collections.unmodifiableList() // 引数にそのまま渡すのは危険 String concatWithLn1 (List<String> namesIn) { ... } // 「そのまま」渡す必要がある String concatWithLn2 (List<String> names) { ... } リストの文字列を連結するメソッド のシグニチャ(Java) サンプルプログラム:XML処理 Twitterのpublicタイムラインのユーザから スクリーン名 フォロワー数 を取得して、フォロワー数(降順)でソートして表示 val timeline = XML.load( new URL("http://api.twitter.com/1/statuses/public_timeline.xml") ) val info = for(user <- timeline ¥¥ "user"; name <- user ¥¥ "screen_name"; follower <- user ¥¥ "followers_count" ) yield (name.text, follower.text) val sorted = info.sortBy{ case (name, followers) => - followers.toInt } for ((name, count) <- sorted) { printf("Name: %s%n", name) printf("Followers: %s%n", count) } Twitterのpublicタイムライン取得プログラム サンプルプログラム:XML処理(実行結果) $ "scala Timeline.scala" で実行可能 コンパイルと実行が同時に行われる Name: PreferKobayashi Followers: 1953 Name: PapaDock_TFF Followers: 1380 Name: dida34 Followers: 1359 Name: DeLaAsia Followers: 699 Name: olivacarden Followers: 444 Name: ReflectSound Followers: 171 Name: _KEYBUMMIE Followers: 167 Name: ElPadrino_2595 Followers: 145 Name: bubych Followers: 144 Name: ranrats Followers: 140 Name: MaryKateMolina Followers: 138 Name: jpii777 Followers: 96 Name: dee_jo_freshh Followers: 94 Name: nova_luna Followers: 87 Name: dedLavanza Followers: 75 Name: RichieMinaj88 Followers: 27 Name: hazelsnutz Followers: 25 Name: cogancool Followers: 7 Name: bellgos6 Followers: 3 Name: telurodebiz Followers: 0 基本ツール(標準添付) scalacコマンド javacに相当(クラスファイルへのコンパイラ) オプションもjavacと類似した部分が多い(-classpath等) fscコマンド scalaコンパイラをサーバとして常駐させて、コンパイルを高速化するコマンド それ以外はscalacと同じ scalaコマンド 1.scalacで生成されたクラスファイルを簡単に実行 2.scalaスクリプト(テキスト)をそのままコンパイル・実行 3.対話環境(REPL) Scala/Java APIの動作を調べたい時に便利 scaladocコマンド javadocコマンド相当 sbazコマンド scalaアプリケーション/ライブラリ用のパッケージマネージャ 18 準標準ライブラリ/フレームワーク sbt Scalaのデファクトスタンダードなビルドツール ビルド設定ファイルをScala DSLとして記述 Apache Ivyベース Mavenリポジトリをそのまま利用できる Mavenと同じディレクトリ構成を採用 簡単な記述で、Scalaの各種ライブラリに対する依存関係を記述可能 ScalaTest/Specs Scalaにおける2大ユニットテスティングライブラリ ScalaTestの方が保守的、Specsは新機能を積極的に取り込む傾向 ScalaCheck (半)自動テスティングライブラリ テスト対象の型からテストケースを自動生成 ScalaTest/Specsを補完 19 sbtのビルド設定ファイル例 単純なプロジェクトなら、これだけでOK No more XML hell 必要であれば、Scalaのプログラムを設定ファイルに書ける // プロジェクト名 name := "Simple Project" // バージョン version := "1.0" // 組織 organization := "hoge" // ビルドに使うScalaのバージョン scalaVersion := "2.9.1" // ライブラリの依存性記述 libraryDependencies ++= Seq( "org.specs2" %% "specs2" % "1.6.1", "org.specs2" %% "specs2-scalaz-core" % "6.0.1" % "test" ) Build.sbt 主要Webアプリケーションフレームワーク/ツールキット Lift Play! Twitter製 Scala用RPCライブラリ JBoss Nettyベース Casbah httpサービスを提供するためのライブラリ/ツールキット(≠Webアプリケーションフレームワーク) no view 簡潔な記述で、URLに対応するサービスをつなげられる Remember the Milk等で採用 Finagle RubyのSinatraフレームワークに影響を受けたフレームワーク 小規模Webアプリケーションの開発に適している フレームワークの仕組みを理解するのが簡単 Unfiltered Java/Scala用Webアプリケーションフレームワーク Ruby on Rails(RoR)ライクな開発サイクル 2.0からコードベースをScalaに移行(Java用 APIは引き続き提供) Scalatra Scala用Webアプリケーションフレームワーク XMLリテラルなどScalaの機能を活用 企業における採用実績一定以上あり (Foursquareなど) Ajax/Cometアプリケーション開発のための便利なAPI MongoDBのScala用API MongoDB開発元の10gen謹製 その他多数 21 主要IDEプラグイン IntelliJ IDEA Scalaプラグイン IDE開発が公式に提供しているプラグイン 最も高機能・安定性が高い Java -> Scala変換のような、マイグレーションのための機能 自動リファクタリング Java/Scala混在プロジェクトもうまく扱える コード補完 Scala IDE for Eclipse Typesafe社に管理が移行 急速に品質が改善 自動リファクタリング(名前変更・メソッド抽出など、現時点では限定的) コード補完 NetBeans Scalaプラグイン 個人での開発 コード補完など、基本機能は揃っている インストールするのがやや面倒 22 Scalaの採用メリット 簡潔なコード 実用的にはJavaの1/3~1/4程度のコード量 冗長なJavaコードを「書く」のはIDEがサポート 冗長なJavaコードを「読む」のは労力が必要 より安全なコード 不変オブジェクトを作りやすい言語設計 Javaでライブラリ化(できない/現実的でない)部分をライブラリ化可能 Don't Repeat Yourself(DRY)原則 リソースの自動クローズ(Loanパターン) Java 7では「言語仕様の拡張」が必要だった Scalaでは、単なるライブラリとして提供できる 一般化: 前処理 -> 本処理 -> 必ず実行される後処理 をライブラリ化可能 ロック不要の並行処理記述ライブラリ(Actor) マルチスレッドプログラムのバグ削減に有効 コレクションライブラリ Java: 標準では「可変」コレクションのみ提供(「読み込み専用」に変換するメソッドはある) 型から「可変」、「読み込み専用」、「不変」のどれを意図しているかがわからない Scala: 標準で、不変コレクション/可変コレクションの両方を提供 型から「可変」、「読み込み専用」、「不変」コレクションのどれかが判別できる 不変コレクションは安全に複数スレッド間で共有可能 領域特化言語(DSL)を簡単に作成できる Actor、パーザライブラリなど 23 Scalaの採用リスク 学習のための良いドキュメント(日本語)不足 「読んですぐScalaを使える」 ドキュメント(チートシート)があまり無い 所謂「コップ本」は速習には向いていない 国内におけるScala開発者の尐なさ 新しい言語には付き物の問題 誮がコードをメンテナンスする? 商用サポート Typesafe社による公式商用サポートは英語前提 日本語の商用サポートは未定 Scalaライブラリにバグがあった場合どうするか 24 リスク回避策(1): Better Javaから始める 言語仕様はJavaよりも複雑 「言語の全てを把握していないと使えない」なら学習コストは高い 言語仕様の複雑さ≠言語の学習コスト(難しさ) Perl, Ruby, PHP等の言語も言語仕様から言えば複雑 全てを最初から覚える必要は無い 「Better Java」から始める 構文を除いて、Javaとの高い互換性を持っている 「JavaプログラムのようなScalaプログラム」はすぐ作れる 要: Java構文 -> Scala構文 のチートシート Java -> Scala自動変換器(IntelliJ IDEA Scalaプラグイン)が存在 型推論・ライブラリの恩恵はすぐに受けられる 「Scalaらしいプログラム」への緩やかな移行が可能 25 リスク回避策(2): 部分的に使い始める JavaソースとScalaソースが混在したプロジェクトをビルド可能 尐しずつScalaを導入できる ユニットテスト用DSL(領域特化言語)としてScalaを導入 ScalaTest/Specs カスタムDSL実装用言語としてScalaを導入 パーザライブラリ等 scalaコマンド(対話環境)の活用 Java APIの挙動をすぐに確かめるのに便利 Scalaの習得のためにも使える 実行結果がすぐ返ってくる 26 Scalaに関するFAQ(1) – 関数型プログラミング Scalaは関数型プログラミングを覚えないと使えない? NO 関数型プログラミングを知っていた方が「より良い」 知らなくても、「新しい静的型付けオブジェクト指向言語」として使える case classによる簡潔なValue Objectの定義 無名クラスのシンタックスシュガー ジェネリックス 強力なコレクションライブラリ 型推論 Option型 オブジェクト指向と関数型のハイブリッド? アカデミックで難しそう 実用的ではない 色々な機能をごちゃまぜ? 学習コストが高い IDEサポートが貧弱? IDEが無いとちょっと… 27 Scalaに関するFAQ(2) – 複雑さと学習曲線 Scalaは複雑なので、習得が難しい? NO 言語仕様の複雑さ ≠ 習得難易度 いわゆるLL(Perl, Ruby, PHP)も言語仕様は「複雑」 言語仕様の全てを知らなくてもプログラムは書ける ただし 汎用Scalaライブラリを開発するなら、ある程度の仕様理解は必要 「汎用ライブラリ設計者」にとっては習得が難しい かもしれない 「ライブラリユーザ」にとっては習得はそれほど難しくない いかにして両者を切り分けるかが誯題 28 Scalaに関するFAQ(3) – 設計思想 Scalaはオブジェクト指向と関数型のハイブリッド? No Javaの仕様をリファクタリングして、型推論やDSL、関数型プログラミング サポート等を統合した静的型付けオブジェクト指向言語 (Java – staticメンバ – プリミティブ型 - …) + 型システムの拡張 + 型 安全なコレクションライブラリ + ... = Scala 追加した部分だけではなく、削った部分がある Scalaはごった煮言語? No, but ... Scalaの設計哲学は「統合」 比較的尐ない概念でより様々な事を表現できるように進化 1つの概念で様々な面を表現可能 概念の数が多いと誤解しがち Scalaは「実用」言語 コミュニティの成長によって、「統合」哲学にそぐわない機能の要望も 結果として、ad hocに見える仕様が追加/維持される事がある 29 Scalaに関するFAQ – IDEサポート IDEサポートが貧弱で使い物にならない? No, but ... 主要IDEに関しては「使える」レベルに達している Eclipse Scalaプラグイン IntelliJ IDEA Scalaプラグイン NetBeans Scalaプラグイン IntelliJ IDEA + Scalaプラグインが最も高機能・安定性が高い しかし 各種IDEのJavaサポートの完成度にはまだ及ばない 自動リファクタリング、補完、コード生成などの面 IDEのサポートをどこまで期待するか 30 デモ Eclipse + Scala IDE for Eclipse NetBeans + Scalaプラグイン IntelliJ IDEA + Scalaプラグイン 31