What is DSL (Domain Specific Language?)
DSL
Google Search
Internal DSL v.s. External DSL
•Internal DSL (内部DSL)
•Written inside an existing host language
既存の言語を使って、その言語の文法を使って書かれたDSL
•No need to implement parser
パーサを書く必要なし
•Some lang are not good at internal DSL
言語によっては苦手な場合がある
•External DSL (外部DSL) Python?
•Original, indendent syntax
独自の文法
•Need to implement parser
パーサを実装する必要あり
•All languages have same power about external DSL
言語による得手不得手は、ほぼない
What is 'with' statement?
•Enforces finishing ## Python 2.4
process f = open('file')
終了処理を強制する機能 try:
•ex: close opened file text = f.read()
certainly finally:
例:ファイルを必ず閉じる f.close()
## Python 2.5 or later
with open('file') as f:
text = f.read()
How to use 'with' statement in DSL?
•Appends pre-
Append pre-process
process and post- 前処理を追加
process around
}
block x=1
ブロックに前処理と後処理をくっ
y=2 code block
つける z=3
Append post-process
後処理を追加
Case Study: Change Directory (Kook)
## before ## after
chdir('build') with chdir('build'):
cp('../file', 'file') cp('../file', 'file')
system('cmd...') system('cmd...')
chdir('..')
Case Study: Start/Stop time (Benchmarker)
## before ## after
t1 = time.time() bm = Benchmarker()
for i in xrange(N): with bm('func'):
func() for i in xrange(N):
t2 = time.time() func()
print('func: %s'
% (t2-t1))
Case Study: Form Builder
## before
<form action="/" method="POST">
<input type="hidden" name="_csrf"
value="${request.csrf()}" />
<input type="text" ... />
<input type="submit" ... />
</form>
Case Study: Form Builder
## after
% with FormBuilder(request, "/") as fb:
<input type="text" ... />
<input type="submit" ... />
% endwith
Pitfall: Variable Scope
with test("arithmetic operation"):
with test("1+1 should be 1"):
x = 1+1
assert x == 2
with test("1-1 should be 0"):
x = 1-1 Identical variable because
assert x == 0 scope is not independented
スコープが独立してないので変数も共有される
Pitfall: Cannot Skip Block!
<div>
% with cache("key", 60*60*1):
<ul>
% for item in get_items():
<li>${item}</li>
% endfor You may want to skip block
</ul> when cache is not expired,
% endwith but Python doesn't allow it!
</div> キャッシュが生きている間はブロックを
スキップしたいんだけど、実はできない
What is 'for' statement?
•Iterates code block ## repeat 100 times
ブロックを繰り返す機能 for i in xrange(100):
•ex: repeat block for N print("i=%s" % i)
times
例:ブロックをN回実行 ## read lines from file
with open('file') as f:
•ex: read each line for line in f:
from opened file
print(line)
例:オープンしたファイルから
1行ずつ読み込む
How to use 'for' statement in DSL? #1
•Appends pre-
Append pre-process
process and post- 前処理を追加
process around
}
iteration block x=1
繰り返し用のブロックに
y=2 iteration block
前処理と後処理をくっつける z=3
Append post-process
後処理を追加
How to use 'for' statement in DSL? #1
•Appends pre-
def gfunc():
process and post-
process around
iteration block
....
.... }pre-process
繰り返し用のブロックに for i in xrange(n):
} exec block
前処理と後処理をくっつける
yield
....
.... } post-process
Case Study: Start/Stop time (Benchmarker)
## before ## after
t1 = time.time() bm = Benchmarker(
for i in xrange(N): repeat=N)
func1() for _ in bm('func'):
t2 = time.time() func1()
print('func: %s'
% (t2-t1))
How to use 'for' statement in DSL? #2
•Iterate code block
def gfunc():
only once
ブロックを1回だけ繰り返す
....
.... } pre-process
yield } exec block
(only once!)
•Emulates 'with' stmt ....
by 'for' stmt .... } post-process
with文をfor文でエミュレート
Case Study: Change Directory (Kook)
## before ## after
chdir('tmp') for _ in chdir('tmp'):
cp('../file', 'file') cp('../file', 'file')
system('cmd...') system('cmd...')
chdir('..')
Use 'for' as alternative
of 'with' in Python 2.4
Python2.4では 'with' が使えない
ので、代わりとして 'for' を使う
How to use 'for' statement in DSL? #3
•Iterate block in 0 or
def gfunc():
1 time
ブロックを0回または1回だけ
繰り返す
....
.... }pre-process
if condition:
}
•Emulates 'if' stmt by
'for' stmt to skip yield exec block
block according to ....
condition
if文をfor文でエミュレートし、条
.... } post-process
件によってブロックをスキップ
Case Study: Fragment Cache (Tenjin)
<div>
% for _ in cache("key", 60*60*1):
<ul>
% for item in get_items():
<li>${item}</li>
% endfor You can skip block when
</ul> cache is not expired! Wow!
% endfor with文と違い、キャッシュが生きている
</div> ときにブロックをスキップできる! ステキ!
Case Study: Java and JSP
<div>
<% for (int _: cache("key",60)) { %>
<ul>
<% for (Item item: getItems()) { %>
<li>${item}</li>
<% } %>
</ul>
<% } %>
</div>
What is Decorator?
•Higher-order function class Hello(object):
to manipulate
declaring function or @classmethod
class def fn(cls):
宣言中の関数やクラスを操作する print("Hello")
高階関数
•ex: @classmethod(), ## above is same as
@property() def fn(cls):
例:@classmethod(),
print("Hello")
@property fn = classmethod(fn)
How to use Decorator in DSL? #1
•Mark function with
def deco(func):
or without args
func._mark = True
関数に目印をつける
return func
def deco_with(arg):
def deco(func):
func._mark = arg
return func
deco
How to use Decorator in DSL? #2
•Syntax sugar to
def fn(arg):
pass function arg
print(arg)
引数として関数を渡すときの
シンタックスシュガー func(x, y, fn)
@func(x, y)
def fn(arg):
print(arg)
Case Study: Event Handler
## before ## after
def _(): @button.onclick
do() def _():
some() do()
thing() some()
button.onclick(_) thing()
More readable because
@onclick appears first
ブロックより先に@onclickが
きているのでより読みやすい
Case Study: Database Transaction
## before ## after
def _(): @transaction
do() def _():
some() do()
thing() some()
transaction(_) thing()
More readable because
@transaction appears first
ブロックより先に@transactionが
現れるので、より読みやすい
Case Study: unittest
## before ## after
def _(): @assertRaise2(Err)
do() def _():
some() do()
thing() some()
assertRaise(Err, _) thing()
More readable because
assertRailse() appears first
assertRaises2()のほうがブロックより
先に現れるので、より読みやすい
How to use Decorator in DSL? #3
•Alternative syntax of
with func(arg) as x:
with- or for-
....
statement
with文やfor文の代替
•Independent scope!
独立したスコープ!
@func(arg)
•Nested scope! def _(x):
入れ子になったスコープ! ....
Case Study: open()
## before ## after
with open('file', @open('file', 'w')
'w') as f: def _(f):
f.write("....") f.write("....")
How to use Decorator in DSL? #4
•Alternative syntax of
class Foo(object):
class definition
def meth1(self):
class定義の代替
....
•More natural
representation of
inheritance structure
@classdef
継承構造のより自然な表現
def _():
•More natural scope @methoddef
より自然なスコープ
def _():
....
Class Inheritance
class Parent(object):
....
class Child(Parent):
....
Inheritance structure
class Baby(Child): is not represented
.... visually
継承構造が視覚的には
表現されていない
Nested Class Definition
class Parent(object):
class Child(Parent):
Represents
class Baby(Child): inheritance structure
.... visually, but not
possible in Python!
こう書けば継承構造が視
覚的に表現できるけど、
Pythonではこう書けない!
Nested Structure by with-statement
with define('Parent'):
with define('Child'):
with define('Baby'): Represents structure
.... visually, but scopes
are not independent
構造は視覚的に表現でき
ているが、スコープが独
立していない (共有される)
Nested Structure with Decorator
@defclass
def Parent():
@defclass
def Child(): Represents structure
visually, and scopes
@defclass are not shared
def Baby(): 継承構造を視覚的に表現
できるし、スコープも共
.... 有されない (独立している)
Strange Scope Rule (for beginner) in Class Definition
1: class Outer(object):
2: VAR = 1
3:
4: class Inner(object):
5: # error
6: print(VAR) Why I can't access
7: # error, too to outer variable?
8: print(Outer.VAR) どうして外側の変数に
アクセスできないの?
Natural Scope Rule (for beginner) with Function
1: def outer():
2: VAR = 1
3:
4: def middle():
5: print(VAR) # OK More natual
6: scope rule
7: def inner(): (especially for beginner)
8: print(VAR) # OK より自然なスコープルール
(特に初心者にとっては)
Case Study: Modern O/R Mapper (SQLAlchemy)
Python SQL
==
x == 1 x=1
x 1
==
x == None x is null
x None
Case Study: Assertion (Oktest)
Assertion When Failed
## Python AssertionError:
assert x == y (no message)
## Nose AssertionError:
ok_(x == y) (no message)
## Oktest AssertionError:
ok (x) == y 1 == 2 : failed
Shows actual & expected values
失敗時に、実際値と期待値を表示してくれる
Case Study: String Interpolation (Kook)
http://www.kuwata-lab.com/kook/pykook-users-guide.html#cookbook-prod
CMD = 'rst2html -i utf-8'
@recipe('hello')
@ingreds('hello.c')
def compile(c):
prod, ingreds = c.product, c.ingreds
system(c%"$(CMD) $(ingreds[0]) > $(prod)")
Operator Indexing
override Both global and available
演算子オーバーライド local var available 添字も利用可能
グローバル変数とローカル
変数の両方が利用可能
Case Study: Method Chain Finishing Problem
AppointmentBuilder()
.From(1300)
.To(1400)
.For("Dental")
.done()
Method chain requires end of chain.
メソッドチェーンでは、チェーンの終わりを明示する必要がある
Case Study: Method Chain Finishing Problem
- AppointmentBuilder()
.From(1300)
.To(1400)
.For("Dental")
Unary operator works at end of chain!
単項演算子はチェーンの終わりに実行される!
Very similar to unordered list or bullet!
見た目が箇条書きそっくり!
Reflected operator
def __add__(self, other):
...
int.__add__ = __add__
1 + Foo() Not possible ;<
Pythonではできない
class Foo(object):
def __radd__(self, other):
... No problem :)
1 + Foo() 問題なし
Case Study: Should DSL
http://www.should-dsl.info/
def test1(self):
self.player |should| have(11).cards
def test2(self):
self.player.name |should|
equal_to('John Doe')
Instead of adding method to any object,
use bit operator which has low priority
ビットOR演算子の優先順位が低いことを利用して、任意のオブジェクトに
メソッドを追加できないというPythonの欠点を克服した、絶妙なDSL