28
Scala DSLの作り方 2010518浅海智晴 (Lab) 2010518日火曜日

Scala DSLの作り方

Embed Size (px)

DESCRIPTION

JJUG CCC 2010 Spring

Citation preview

Page 1: Scala DSLの作り方

Scala DSLの作り方

2010年5月18日浅海智晴 (匠Lab)

2010年5月18日火曜日

Page 2: Scala DSLの作り方

Scala

•純粋なオブジェクト指向言語

•本格的な関数型言語

•静的型付け&型パラメータ&型推論

•スクリプト言語的な使いやすさ

• JVM上で動作⇒Javaの資産をそのまま活用

• Scalable ≒ DSLのホスト言語に最適

2010年5月18日火曜日

Page 3: Scala DSLの作り方

DSL

• Domain Specific Language

•領域特化言語

•専用言語で必要十分な情報を効率良く記述

•ツールで必要な成果物を生成

• OSMU (One Source Multi Use)

2010年5月18日火曜日

Page 4: Scala DSLの作り方

Scala DSL

2010年5月18日火曜日

Page 5: Scala DSLの作り方

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日火曜日

Page 6: Scala DSLの作り方

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日火曜日

Page 7: Scala DSLの作り方

SimpleModelerが生成したクラス図

2010年5月18日火曜日

Page 8: Scala DSLの作り方

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日火曜日

Page 9: Scala DSLの作り方

ケーススタディ

• 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日火曜日

Page 10: Scala DSLの作り方

ケーススタディ: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日火曜日

Page 11: Scala DSLの作り方

DSL実装

2010年5月18日火曜日

Page 12: Scala DSLの作り方

抽象クラス&インスタンス変数

class MyTodo extends Todo { name = "山田太郎"

}

abstract class Todo { var name: String = _}

2010年5月18日火曜日

Page 13: Scala DSLの作り方

メソッド

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日火曜日

Page 14: Scala DSLの作り方

メソッド・チェイン

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)

Page 15: Scala DSLの作り方

暗黙変換

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日火曜日

Page 16: Scala DSLの作り方

階層構造を導入

class MyTodo extends Todo { name = "山田太郎"

"ボールペンを買ってくる。" until 20100505

"図書館に本を返してくる。" until 20100606

"定期券を更新する。" until 20100707

task("開発環境を整える。") {

todo("PCをインストールする。")

"IDEをインストールする。" until 20100707

}}

2010年5月18日火曜日文字列だけだと暗黙変換されない、という制約がある。

Page 17: Scala DSLの作り方

階層構造を導入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を用いて多層構造にしたいところだけど、例題を簡単にするために単層にしている。

Page 18: Scala DSLの作り方

階層構造でのパラメタの受け取り

class MyTodo extends Todo { name = "山田太郎"

"ボールペンを買ってくる。" until 20100505

"図書館に本を返してくる。" until 20100606

"定期券を更新する。" until 20100707

task("開発環境を整える。") {

until = 20100808 todo("PCをインストールする。")

"IDEをインストールする。" until 20100707

}}

2010年5月18日火曜日

Page 19: Scala DSLの作り方

階層構造でのパラメタの受け取り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日火曜日

Page 20: Scala DSLの作り方

処理系実装

2010年5月18日火曜日

Page 21: Scala DSLの作り方

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日火曜日

Page 22: Scala DSLの作り方

前処理

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日火曜日

Page 23: Scala DSLの作り方

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日火曜日

Page 24: Scala DSLの作り方

リスト処理

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日火曜日実装例を簡略化するためロジックの一部を省略している。

Page 25: Scala DSLの作り方

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日火曜日

Page 26: Scala DSLの作り方

その他の技法

•ケースクラス

•コンパニオンオブジェクト

•ファクトリメソッド

•抽出子

• applyメソッド

• updateメソッド

•アノテーション

•生文字リテラル

• XMLリテラル

•ケースシーケンス

•名前付きパラメタ (2.8)

•デフォルトパラメタ (2.8)

2010年5月18日火曜日

Page 27: Scala DSLの作り方

まとめ

• DSLを導入するならScalaがオススメ

• Scalaは、Java的に使っている分には思ったよりも難しくない…

•というより簡単、便利

• Scala DSLもやってみると案外簡単

•ただ、今のところはまとまった資料はないみたい

•まずはScalaを使ってみてください

2010年5月18日火曜日

Page 28: Scala DSLの作り方

End

2010年5月18日火曜日