Tagless Final DSL
-
Upload
yyu -
Category
Technology
-
view
918 -
download
0
Transcript of Tagless Final DSL
![Page 1: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/1.jpg)
1
Tagless Final DSL
吉村 優( @_yyu_ )
![Page 2: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/2.jpg)
注意• この発表には Free モナドが含まれています• Free モナドを初めて聞く方は、自己紹介など
のスキにググっておくと、スムーズです• このスライドの Scala のようなプログラム言語
は、スペースの都合で省略されて部分があるので、実際には動きませんo https://github.com/yoshimuraYuu/DIwithTaglessFinal
2
![Page 3: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/3.jpg)
自己紹介• Twitter
https://twitter.com/_yyu_
• GitHubhttps://github.com/yoshimuraYuu
• Qiitahttp://qiita.com/yyu
関数型言語( Scala, OCaml )、暗号、ときどき組版など
3
![Page 4: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/4.jpg)
DSL とは?“DSL は一種類のタスクをうまく実行するこ
とに集中した言語である„Wikipedia
主な DSL としてo SQLo Yacco Make
4
![Page 5: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/5.jpg)
Embedded DSL“ プログラム言語の内部に書かれた DSL のこ
と。 EDSL とも言う„主な EDSL として
o LINQo jQuery
5
![Page 6: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/6.jpg)
EDSL を作る• Twitter を操作する Scala の EDSL
• まずは Free モナドによる伝統的なアプローチで作成
• 次に Tagless Final による実装
6
![Page 7: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/7.jpg)
完成予定図• 指定したスクリーンネームのユーザー情報を取得
o存在すればexistとツイートoそうでなければnot existとツイート
val dsl = fetch( "_yyu_", res => if (res.status == 200) update("exist") else update("not exist"))runTwitter(dsl)
7
![Page 8: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/8.jpg)
代数的データ型sealed trait Twitter[A]
case class Fetch[A](sn: String, next: WSResponse => A) extends Twitter[A]
case class Update[A](status: String, next: A) extends Twitter[A]
• sn はスクリーンネーム• next は次の処理を入れておくための場所• WSResponse は HTTP のレスポンスを表わす型
8
![Page 9: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/9.jpg)
Twitter をファンクタへimplicit val tf = new Functor[Twitter] { def map[A, B](a: Twitter[A])(f: A => B) = a match { case Fetch(sn, next) =>
Fetch(sn, x => f(next(x))) case Update(status, next) =>
Update(status, f(next)) }}
• Free モナドを使うための準備
9
![Page 10: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/10.jpg)
EDSL の定義def fetch[A](sn: String, f: WSResponse => Free[Twitter, A]): Free[Twitter, A] = More(Fetch(sn, f))
def update(s: String): Free[Twitter, Unit]= More(Update(status, Done()))
• Twitter をファンクタにしたので、 Free モナドを使うことができる
• Free モナドで Twitter[Twitter[A]] のような型を回避
10
![Page 11: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/11.jpg)
インタープリターdef runTwitter[A](dsl: Free[Twitter, A]): Unit = dsl match { case Done(a) => () case More(Fetch(screenName, next)) => val fws = fetchUserByScreenName(screenName) runTwitter(next(fws), env) case More(Update(status, next)) => updateStatus(status) runTwitter(next, env) }
• 数珠繋ぎになった DSL を順に実行
具体的な実装
具体的な実装
11
![Page 12: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/12.jpg)
完成val dsl = fetch( "_yyu_", res => if (res.status == 200) update("exist") else update("not exist"))runTwitter(dsl)
12
![Page 13: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/13.jpg)
Taglees Final Approach
13
![Page 14: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/14.jpg)
Tagless FinalEDSL をつくるための手法
• Free モナドを使った手法との違いoGADT (一般化代数的データ型)が不要oHOAS (高階抽象構文)が使えるoExpression Problem の回避o型付けをホスト言語に帰着できる
14
![Page 15: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/15.jpg)
インターフェースDSL のインターフェースを型クラスとして提供
case class Twitter[A](v: A)
object Twitter { def twitter[A](a: A): Twitter[A] = twitter(a)}
trait TwitterSYM[R[_]] { def string(str: String): R[String] def fetch(sn: R[String]): R[WSResponse] def getSN(res: R[WSResponse]): R[String] def update(status: R[String]): R[String]}
15
![Page 16: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/16.jpg)
インスタンスDSL を型クラスのインスタンスとして実装
implicit val twitterSYMInterpreter = new TwitterSYM[Twitter] { def getSN(res: Twitter[WSResponse]) = val raw = res.v twitter(
( raw.json \ "screen_name").as[String] ) }
16
![Page 17: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/17.jpg)
実装DSL を型クラスのインスタンスとして実装
def getSN (res: Twitter[WSResponse]) (implicit T: TwitterSYM[Twitter]) = T.getSN(res)
17
![Page 18: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/18.jpg)
Tagless Final DSL普通の関数のように呼び出し
getSN( fetch(string("_yyu_"))).v
18
![Page 19: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/19.jpg)
拡張インターフェースが型クラスなので拡張が容易
// 新しい型クラスtrait DeleteSYM[R[_]] { def delete(id: R[String]): R[Boolean]}
19
![Page 20: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/20.jpg)
拡張インターフェースが型クラスなので拡張が容易
// 実装implicit val deleteInterpreter = new DeleteSYM[Twitter] { def delete(id: Twitter[String]): Twitter[Boolean] =
??? }
def delete(id: Twitter[String]) (implicit T: DeleteSYM[Twitter]) = T.delete(id)
20
![Page 21: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/21.jpg)
HOAS次のような let 構文を EDSL に入れたい
let a = string("_yyu_") inlet b = fetch(a) in let c = getScreeName(b) in let d = update(c) in delete(d)
このような変数の管理を実装するのは大変
スクリーンネームを代入
ユーザーの情報を取得取得したユーザーの
スクリーンネームを取得
スクリーンネームをツイート ツイートを削除
21
![Page 22: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/22.jpg)
インターフェース変数の束縛をホスト言語に任せる
trait LetInSYM[R[_]] { def let[A, B](a: => R[A])(l: R[A => B]): R[B] def in[A, B](a: R[A] => R[B]): R[A => B]}
22
![Page 23: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/23.jpg)
実装def let[A, B](a: => Twitter[A])(tf: Twitter[A => B]): Twitter[B] = tf.v(a.v)
def in[A, B](f: Twitter[A] => Twitter[B]): Twitter[A => B] = { twitter( (x: Twitter[A]) => f(x) )}
23
![Page 24: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/24.jpg)
完成let (string("_yyu_")) (in (a =>let (fetch(a)) (in (b =>let (getScreeName(b)) (in (c =>let (update(c)) (in (d => delete(d)))))))))
• 括弧の数が少々えぐい……
24
![Page 25: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/25.jpg)
まとめ• Tagless Final は Free モナドを使ったアプロー
チと同じものを表現できる• HOAS や型付けの帰着など、上手く使えば Free
モナドのアプローチよりも高い表現力が得られる
• しかし表現力の高い DSL はもはやL ( Language )という議論もある
25
![Page 26: Tagless Final DSL](https://reader035.fdocuments.net/reader035/viewer/2022081604/58874ba61a28ab5a628b635f/html5/thumbnails/26.jpg)
おわり
26