Upload
tomoharu-asami
View
4.770
Download
2
Embed Size (px)
DESCRIPTION
JJUG CCC 2010 Spring
Citation preview
Scala DSLの作り方
2010年5月18日浅海智晴 (匠Lab)
2010年5月18日火曜日
Scala
•純粋なオブジェクト指向言語
•本格的な関数型言語
•静的型付け&型パラメータ&型推論
•スクリプト言語的な使いやすさ
• JVM上で動作⇒Javaの資産をそのまま活用
• Scalable ≒ DSLのホスト言語に最適
2010年5月18日火曜日
DSL
• Domain Specific Language
•領域特化言語
•専用言語で必要十分な情報を効率良く記述
•ツールで必要な成果物を生成
• OSMU (One Source Multi Use)
2010年5月18日火曜日
Scala DSL
2010年5月18日火曜日
Specs
• BDD• Behavior Driven
Development
• コンポーネントの振舞いを自然言語に近い形の実行可能なテストケースという形式で記述する
• Specs
• Scalaのテスティングフレームワークの一つ
object CsvEntityTest extends Specification { val TestReadDir = "/tmp/goldenport.d/read" val TestCreateDir = "/tmp/goldenport.d/create/CsvEntity" val readonlyCsvFileName = GoldenportTestUtility.readonlyCsvFileName
"CsvEntityのRead" should {
"CsvEntityをDefaultEntitySpace経由でFileDataSourceからopen" in {
val space = new DefaultEntitySpace space.addEntityClass(new CsvEntityClass()) val datasource = new FileDataSource(readonlyCsvFileName, space.context) val mayCsv: Option[GEntity] = space.reconstitute(datasource) mayCsv must beSome[GEntity] val csv = mayCsv.get.asInstanceOf[CsvEntity] csv.open() csv.width must be(3) csv.height must be(2) csv.get(0, 0) must be_==("A") csv.get(1, 0) must be_==("B") csv.get(2, 0) must be_==("C") csv.get(0, 1) must be_==("X") csv.get(1, 1) must be_==("Y") csv.get(2, 1) must be_==("Z") csv.close() }
2010年5月18日火曜日
SimpleModeler
•浅海が開発中のDSLコンパイラ
• Scala DSLから、クラス図、状態機械図、状態遷移表、Google App
Engine Javaプログラムなどを生成する
case class DER製品 extends DomainResource { term = "製品" caption = "製品" brief = <t></t> description = <text></text>
id("製品Id", DVI製品Id()) attribute("製品Name", DVN製品Name())}
case class DVI製品Id extends DomainValueId { term = "製品Id" caption = "製品Id" brief = <t></t> description = <text></text>
attribute("value", XString)}
2010年5月18日火曜日
SimpleModelerが生成したクラス図
2010年5月18日火曜日
g3フレームワーク
• 浅海が開発中のメッセージフロー・フレームワーク
• Scala DSLで記述したメッセージフローを実行する
class Join extends G3Application { agent('compute) { case x: Int => x + 100 }
start(List(1, 2, 3, 4, 5)) split() publish("compute") join() aggregate()}
2010年5月18日火曜日
ケーススタディ
• TODO管理のための
Scala DSL
• Scala DSLからHTMLを生成
class MyTodo extends Todo { name = "山田太郎"
"ボールペンを買ってくる。" until 20100505
"図書館に本を返してくる。" until 20100606
"定期券を更新する。" until 20100707
task("開発環境を整える。") {
until = 20100808 todo("PCをインストールする。")
"IDEをインストールする。" until 20100707
}}
2010年5月18日火曜日
ケーススタディ:DSLから生成したHTML
<html><head> <title>TODO一覧</title>
</head><body>
<ul> <li>ボールペンを買ってくる。(20100505)</li>
<li>図書館に本を返してくる。(20100606)</li>
<li>定期券を更新する。(20100707)</li>
<li>PCをインストールする。(20100808) [開発環境を整える。]</li>
<li>IDEをインストールする。(20100808) [開発環境を整える。]</li>
</ul>
</body></html>
2010年5月18日火曜日
DSL実装
2010年5月18日火曜日
抽象クラス&インスタンス変数
class MyTodo extends Todo { name = "山田太郎"
}
abstract class Todo { var name: String = _}
2010年5月18日火曜日
メソッド
class MyTodo extends Todo { name = "山田太郎"
todo("ボールペンを買ってくる。")
todo("図書館に本を返してくる。")
todo("定期券を更新する。")
}
import scala.collection.mutable.ArrayBuffer
abstract class Todo { var name: String = _ val items = new ArrayBuffer[TodoItem]
def todo(title: String) { val item = new TodoItem(title) items += item }}
class TodoItem(val title: String) {}
2010年5月18日火曜日
メソッド・チェイン
class MyTodo extends Todo { name = "山田太郎"
todo("ボールペンを買ってくる。") until 20100505
todo("図書館に本を返してくる。") until 20100606
todo("定期券を更新する。") until 20100707
}
import scala.collection.mutable.ArrayBuffer
abstract class Todo { var name: String = _ val items = new ArrayBuffer[TodoItem]
def todo(title: String): TodoItem = { val item = new TodoItem(title) items += item item }}
class TodoItem(val title: String) { var untilDate: Int = _
def until(date: Int): TodoItem = { untilDate = date this }}
2010年5月18日火曜日流れるようなインタフェース(fluent interface)
暗黙変換
class MyTodo extends Todo { name = "山田太郎"
"ボールペンを買ってくる。" until 20100505
"図書館に本を返してくる。" until 20100606
"定期券を更新する。" until 20100707
}
import scala.collection.mutable.ArrayBuffer
abstract class Todo { var name: String = _ val items = new ArrayBuffer[TodoItem]
def todo(title: String): TodoItem = { val item = new TodoItem(title) items += item item }
implicit def todoWrapper(title: String): TodoItem = todo(title)}
class TodoItem(val title: String) { var untilDate: Int = _
def until(date: Int): TodoItem = { untilDate = date this }}
2010年5月18日火曜日
階層構造を導入
class MyTodo extends Todo { name = "山田太郎"
"ボールペンを買ってくる。" until 20100505
"図書館に本を返してくる。" until 20100606
"定期券を更新する。" until 20100707
task("開発環境を整える。") {
todo("PCをインストールする。")
"IDEをインストールする。" until 20100707
}}
2010年5月18日火曜日文字列だけだと暗黙変換されない、という制約がある。
階層構造を導入import scala.collection.mutable.ArrayBuffer
abstract class Todo { var name: String = _ val items = new ArrayBuffer[TodoItem] val tasks = new ArrayBuffer[Task] private[this] var currentTask: Option[Task] = None
def todo(title: String): TodoItem = { val item = new TodoItem(title) currentTask match { case Some(tk) => tk.items += item case None => items += item } item }
def task(title: String)(p: => Unit): Task = { val tk = new Task(title) tasks += tk currentTask = Some(tk) p currentTask = None tk }
implicit def todoWrapper(title: String): TodoItem = todo(title)}
class TodoItem(var title: String) { var untilDate: Int = _
def until(date: Int): TodoItem = { untilDate = date this }}
class Task(var title: String) { val items = new ArrayBuffer[TodoItem] var untilDate: Int = _}
2010年5月18日火曜日本来はStackを用いて多層構造にしたいところだけど、例題を簡単にするために単層にしている。
階層構造でのパラメタの受け取り
class MyTodo extends Todo { name = "山田太郎"
"ボールペンを買ってくる。" until 20100505
"図書館に本を返してくる。" until 20100606
"定期券を更新する。" until 20100707
task("開発環境を整える。") {
until = 20100808 todo("PCをインストールする。")
"IDEをインストールする。" until 20100707
}}
2010年5月18日火曜日
階層構造でのパラメタの受け取りimport scala.collection.mutable.ArrayBuffer
abstract class Todo { var name: String = _ val items = new ArrayBuffer[TodoItem] val tasks = new ArrayBuffer[Task] private[this] var currentTask: Option[Task] = None
def todo(title: String): TodoItem = { val item = new TodoItem(title) currentTask match { case Some(tk) => tk.items += item case None => items += item } item }
def task(title: String)(p: => Unit): Task = { val tk = new Task(title) tasks += tk currentTask = Some(tk) p currentTask = None tk } this }}
def until: Int = { currentTask.get.untilDate }
def until_=(value: Int) { currentTask.get.untilDate = value }
implicit def todoWrapper(title: String): TodoItem = todo(title)}
class TodoItem(var title: String) { var untilDate: Int = _
def until(date: Int): TodoItem = { untilDate = date this }}
class Task(var title: String) { val items = new ArrayBuffer[TodoItem] var untilDate: Int = _}
2010年5月18日火曜日
処理系実装
2010年5月18日火曜日
MakeTodoDslコマンドimport scala.xml.{XML, Node}
object MakeTodoDsl { def main(args: Array[String]) { val input = args(0) val output = args(1)
val appClass = Class.forName(input) val todo = appClass.newInstance.asInstanceOf[Todo]
val html = <html><head> <title>TODO一覧</title>
</head><body>
<ul> { makeList(todo) }</ul>
</body></html>
XML.save(output, html, "utf-8") }
private def makeList(todo: Todo): List[Node] = { (for (item <- todo.items) yield <li>{ "%s(%s)".format(item.title, item.untilDate) }</li>).toList ::: (for (task <- todo.tasks; item <- task.items) yield <li>{ "%s(%s) [%s]".format(item.title, task.untilDate, task.title) }</li>).toList }}
2010年5月18日火曜日
前処理
def main(args: Array[String]) { val input = args(0) val output = args(1)
val appClass = Class.forName(input) val todo = appClass.newInstance.asInstanceOf[Todo]
2010年5月18日火曜日
XML
val html = <html><head> <title>TODO一覧</title>
</head><body>
<ul> { makeList(todo) }</ul>
</body></html>
XML.save(output, html, "utf-8")}
2010年5月18日火曜日
リスト処理
private def makeList(todo: Todo): List[Node] = { (for (item <- todo.items) yield <li>{ "%s(%s)".format(item.title, item.untilDate) }</li>).toList ::: (for (task <- todo.tasks; item <- task.items) yield <li>{ "%s(%s) [%s]".format(item.title, task.untilDate, task.title)}</li> ).toList }
2010年5月18日火曜日実装例を簡略化するためロジックの一部を省略している。
DSLから生成したHTML
•ちょっと手で整形しています。
<html><head> <title>TODO一覧</title>
</head><body>
<ul> <li>ボールペンを買ってくる。(20100505)</li>
<li>図書館に本を返してくる。(20100606)</li>
<li>定期券を更新する。(20100707)</li>
<li>PCをインストールする。(20100808) [開発環境を整える。]</li>
<li>IDEをインストールする。(20100808) [開発環境を整える。]</li>
</ul>
</body></html>
2010年5月18日火曜日
その他の技法
•ケースクラス
•コンパニオンオブジェクト
•ファクトリメソッド
•抽出子
• applyメソッド
• updateメソッド
•アノテーション
•生文字リテラル
• XMLリテラル
•ケースシーケンス
•名前付きパラメタ (2.8)
•デフォルトパラメタ (2.8)
2010年5月18日火曜日
まとめ
• DSLを導入するならScalaがオススメ
• Scalaは、Java的に使っている分には思ったよりも難しくない…
•というより簡単、便利
• Scala DSLもやってみると案外簡単
•ただ、今のところはまとまった資料はないみたい
•まずはScalaを使ってみてください
2010年5月18日火曜日
End
2010年5月18日火曜日