Slide 1

Slide 1 text

Copyright © Sansan, Inc. All rights reserved. > KotlinでDSL Jumpei Yamamoto 2016.12.13 第4回 Kotlin勉強会 in Sansan #kotlin_sansan

Slide 2

Slide 2 text

Copyright © Sansan, Inc. All rights reserved. > ⾃⼰紹介 - ⼭本純平 - Sansan株式会社 Eight事業部 - EightのAndroidアプリの開発 - twitter: @boohbah - github: https://github.com/yamamotoj

Slide 3

Slide 3 text

Copyright © Sansan, Inc. All rights reserved. > Pikkel • AndroidでIcePickのように状態の保存ができるKotlinの ライブラリ • https://github.com/yamamotoj/Pikkel

Slide 4

Slide 4 text

Copyright © Sansan, Inc. All rights reserved. > Pikkel class MainActivity : Activity(), Pikkel by PikkelDelegate() { var data by state(“”) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) restoreInstanceState(savedInstanceState) } override fun onSaveInstanceState(outState: Bundle?) { super.onSaveInstanceState(outState) saveInstanceState(outState) } }

Slide 5

Slide 5 text

Copyright © Sansan, Inc. All rights reserved. 5

Slide 6

Slide 6 text

Copyright © Sansan, Inc. All rights reserved. > Eight

Slide 7

Slide 7 text

Copyright © Sansan, Inc. All rights reserved. > Agenda - テーマ:KotlinでDSL - DSLとは - KotlinでDSLの実践

Slide 8

Slide 8 text

Copyright © Sansan, Inc. All rights reserved. > DSL ドメイン固有⾔語 - Domain Specific Language - “特定のドメインに集中し、限定された表現⼒ を備えたコンピュータプログラミング⾔語” by 「ドメイン特化⾔語」マーチン・ファウラー著 - ⼤きく分けて外部DSLと内部DSLがある

Slide 9

Slide 9 text

Copyright © Sansan, Inc. All rights reserved. > 外部DSL - 動作するアプリケーションとは別の独⽴した⾔語 - 独⾃に構⽂解析、字句解析を⾏い、必要な機能を実装す る。 - 基本的に独⾃の構⽂を持つが、XML等の他の⾔語の構⽂を 利⽤することもある。 CSSの例

Slide 10

Slide 10 text

Copyright © Sansan, Inc. All rights reserved. > 内部DSLとは - アプリケーションの⾔語を 特定のドメイン向けにそれっ ぽく使う。 - 表現能⼒ (== 読みやすさ)はホスト⾔語の制約に依存 - 「流れるようなインターフェイス」⊂ 内部DSL - 動的型付⾔語(Rubyとか)と静的型付⾔語(Javaとか Kotlinとか)で実装の⽅法が異なる

Slide 11

Slide 11 text

Copyright © Sansan, Inc. All rights reserved. > 内部DSLの例 - Ruby on rails, rake, gradle - Anko,

Slide 12

Slide 12 text

Copyright © Sansan, Inc. All rights reserved. > 静的型付け⾔語で > DSLを作る上でのポイント - 適切にドメインの語彙を使⽤しているか? - 読み⼿が(余計な複雑さなしに)いかに⾃然に⾔語を読み 下せるか? - 型付けによって以下に適切にIDEの補完を効かせることが できるか?

Slide 13

Slide 13 text

Copyright © Sansan, Inc. All rights reserved. > JavaのBuilderパターン People tom = new People.Builder() .name(“Tom”) .age(12) .hobby(“Baseball”) .build(); tom.hello(); これも⽴派なDSL new や .Builder()….build()等 ドメインとは無関係の記述が複雑 ()とか;とか冗⻑

Slide 14

Slide 14 text

Copyright © Sansan, Inc. All rights reserved. > Kotlin 名前付き引数 / デフォルト引数 class People( name:String, age:Int, hobby:String = “” ) val tom = People( age = 12, name = “tom” ) 引数に名前をつける ことで、読み⼿がど の値を設定している かが⼀⽬瞭然 設定する順番も変え られる optionalな項⽬ (hobby)は省略可

Slide 15

Slide 15 text

Copyright © Sansan, Inc. All rights reserved. > DSL実践編

Slide 16

Slide 16 text

Copyright © Sansan, Inc. All rights reserved. > SMC ( State Machine Compiler) //State Start { // Transition / Next State / Action(s) TO_ZERO Zero {} } Zero { TO_ONE One {} } One { Return Start {} } http://smc.sourceforge.net/ 状態遷移をDSL記述してJavaやRubyなどのコードを⽣成 外部DSLとして実装 状態の定義 イベント名 遷移先の状態 アクション

Slide 17

Slide 17 text

Copyright © Sansan, Inc. All rights reserved. > DSLを実装する⼿順 - DSLによって実現したいモデル構造(セマンティックモデ ル)を定義する。 - どういう構⽂を定義するかを考える - 流れるようなインターフェイス? - 階層構造 - 構⽂の評価、改善を繰り返す

Slide 18

Slide 18 text

Copyright © Sansan, Inc. All rights reserved. > SMC ( State Machine Compiler ) セマンティックモデル StateMachine State State State … Event Event … Event

Slide 19

Slide 19 text

Copyright © Sansan, Inc. All rights reserved. > Kotlinでモデル構造の定義 class StateMachine( var currentState: State, val states: Map) class State( val name: String, val events: Map) class Event( val nextState: String, val action: (()-> Unit)?)


Slide 20

Slide 20 text

Copyright © Sansan, Inc. All rights reserved. > SMCの構⽂ //State // Transition / Next State / Action(s) Start { TO_ZERO Zero {} } Zero { TO_ONE One {} } One { Return Start {} } 可能な限りこれをKotlinで表現してみる

Slide 21

Slide 21 text

Copyright © Sansan, Inc. All rights reserved. > Kotlinで定義したSMCの構⽂ val sm = StateMachine.define { “Start"{ "TO_ZERO" transitTo "Zero" action { } } “Zero"{ "TO_ONE" transitTo "One" action { } } “One"{ "RETURN" transitTo "Start" action { } } } 状態の定義 イベント名 遷移先の状態 アクション

Slide 22

Slide 22 text

Copyright © Sansan, Inc. All rights reserved. > Kotlin SMC val sm = StateMachine.define { “Start"{ "TO_ZERO" transitTo "Zero" action { } } “Zero"{ "TO_ONE" transitTo "One" action { } } “One"{ "RETURN" transitTo "Start" action { } } } この構⽂がどのように実装されるか解説していきます。

Slide 23

Slide 23 text

Copyright © Sansan, Inc. All rights reserved. > SMCを実装するクラス構成 StateMachineBuilder (内部DSLの実装) StateBuilder (内部DSLの実装) EventBuilder (内部DSLの実装) 1 * 1 * StateMachine State Event 1 * 1 * build() build() build()

Slide 24

Slide 24 text

Copyright © Sansan, Inc. All rights reserved. > StateMachineの定義 val sm = StateMachine.define { “Start"{ "TO_ZERO" transitTo "Zero" action { } } “Zero"{ "TO_ONE" transitTo "One" action { } } “One"{ "RETURN" transitTo "Start" action { } } } class StateMachine { companion object{ fun define(def:StateMachineBuilder.() -> Unit) : StateMachine { /* … */}
 }
 
 } 引数にラムダ式がひとつだけの場合は()を省略可 レシーバー指定の関数型 StateMachineBuilderの拡張関数を引数にとる StateMachineBuilderにて他のクラスの拡張関数を定義し ている場合にその拡張関数のスコープを限定することがで きる

Slide 25

Slide 25 text

Copyright © Sansan, Inc. All rights reserved. > StateMachineBuilderの実装 class StateMachineBuilder { private val states = mutableMapOf() private var initialStateName:String? = null operator fun String.invoke(define: StateBuilder.() -> Unit){ 
 states[this] = StateBuilder(this).apply(define) 
 if(initialStateName == null){ 
 initialStateName = this } }
 
 }


Slide 26

Slide 26 text

Copyright © Sansan, Inc. All rights reserved. > invoke operator class A { operator fun invoke(){ print(“this is a”) } } val a = A() a() // this is a A()() // this is a

Slide 27

Slide 27 text

Copyright © Sansan, Inc. All rights reserved. > invoke operator class A { operator fun invoke( arg: String){ print(“this is a with $arg”) } } val a = A() a(“foo”) // this is a with foo A()(“bar”) // this is a with bar

Slide 28

Slide 28 text

Copyright © Sansan, Inc. All rights reserved. > invoke operator // ֦ுؔ਺ͱ࣮ͯ͠૷ operator fun String.invoke(){ print(“this is $this”) } “foo”() // this is foo

Slide 29

Slide 29 text

Copyright © Sansan, Inc. All rights reserved. > invoke operator // ϥϜμࣜΛҾ਺ʹͱΔ operator fun String.invoke(f: String -> Unit){ f(this) } “foo”{ print(it) }

Slide 30

Slide 30 text

Copyright © Sansan, Inc. All rights reserved. > StateMachineBuilderの実装 class StateMachineBuilder { private val states = mutableMapOf() private var initialStateName:String? = null operator fun String.invoke(define: StateBuilder.() -> Unit){ 
 states[this] = StateBuilder(this).apply(define) 
 if(initialStateName == null){ 
 initialStateName = this } }
 
 }
 invoke operator Ϩγʔόʔࢦఆͷؔ਺ܕ

Slide 31

Slide 31 text

Copyright © Sansan, Inc. All rights reserved. > invoke operatorを使った構⽂ val sm = StateMachine.define { “Start"{ "TO_ZERO" transitTo "Zero" action { } } “Zero"{ "TO_ONE" transitTo "One" action { } } “One"{ "RETURN" transitTo "Start" action { } } }

Slide 32

Slide 32 text

Copyright © Sansan, Inc. All rights reserved. > StateBuilderの実装 class StateBuilder(val name:String) { private val events = mutableMapOf() infix fun String.transitTo(to:String): EventBuilder { val eventBuilder = EventBuilder(this, to) events[this]= eventBuilder return eventBuilder
 } }


Slide 33

Slide 33 text

Copyright © Sansan, Inc. All rights reserved. > 中置関数 class A { infix fun shows(arg:String){ print(arg) } } val a = A() a.shows(“bar”) a shows “bar”

Slide 34

Slide 34 text

Copyright © Sansan, Inc. All rights reserved. > StateBuilderの実装 class StateBuilder(val name:String) { private val events = mutableMapOf() infix fun String.transitTo(to:String): EventBuilder { val eventBuilder = EventBuilder(this, to) events[this]= eventBuilder return eventBuilder
 } fun build() = State(name, events.map { it.key to it.value.build() }.toMap())
 }
 String型の拡張関数

Slide 35

Slide 35 text

Copyright © Sansan, Inc. All rights reserved. > 中置関数を使った構⽂ val sm = StateMachine.define { “Start"{ "TO_ZERO" transitTo "Zero" action { } } “Zero"{ "TO_ONE" transitTo "One" action { } } “One"{ "RETURN" transitTo "Start" action { } } }

Slide 36

Slide 36 text

Copyright © Sansan, Inc. All rights reserved. > EventBuilderの実装 class EventBuilder(val name:String, val transitTo: String){ var action:(()->Unit)? = null infix fun action(action:()-> Unit){ this.action = action } 
 fun build(): Event = Event(transitTo, action) } ラムダ式を引数にとる中置関数

Slide 37

Slide 37 text

Copyright © Sansan, Inc. All rights reserved. > 中置関数を使った構⽂ val sm = StateMachine.define { “Start"{ "TO_ZERO" transitTo "Zero" action { } } “Zero"{ "TO_ONE" transitTo "One" action { } } “One"{ "RETURN" transitTo "Start" action { } } } EventBuilder 中置関数は左結合!

Slide 38

Slide 38 text

Copyright © Sansan, Inc. All rights reserved. > Kotlin SMC 完成 val sm = StateMachine.define { “Start"{ "TO_ZERO" transitTo "Zero" action { } } “Zero"{ "TO_ONE" transitTo "One" action { } } “One"{ "RETURN" transitTo "Start" action { } } }

Slide 39

Slide 39 text

Copyright © Sansan, Inc. All rights reserved. > ソース公開しています。 > https://github.com/yamamotoj/KotlinSMC

Slide 40

Slide 40 text

Copyright © Sansan, Inc. All rights reserved. > KotlinでのDSLの資料 - Anko - https://github.com/Kotlin/anko - KotlinでDSLを作るときに活躍する5⼤⾔語機能 @ngsw_taro - http://taro.hatenablog.jp/entry/ 2013/12/25/070324 - KotlinでイケてるDSLを作る @kikuchy - http://qiita.com/kikuchy/items/ 7c8893c4e7e11dafdb6c

Slide 41

Slide 41 text

$PQZSJHIU˜4BOTBO *OD"MMSJHIUTSFTFSWFE 4BOTBO͸Ұॹʹ৽͍͠Ձ஋Λ࡞͍ͬͯ͘ ஥ؒΛ͕͍ͯ͞͠·͢ɻ 3VCZ 3VCZPO3BJMT ʢ8FCΞϓϦέʔγϣϯʣ $ɼ"41/&5.7$ ʢ8FCΞϓϦέʔγϣϯʣ J04"OESPJEΞϓϦ   ݸਓ޲໊͚ࢗ؅ཧΞϓϦʮ&JHIUʯ   ໊ࢗσʔλԽ෼ࢄॲཧγεςϜ   ๏ਓ޲໊͚ࢗ؅ཧαʔϏεʮ4BOTBOʯ   ๏ਓ޲໊͚ࢗ؅ཧαʔϏε ʮ4BOTBOʯ   ݸਓ޲໊͚ࢗ؅ཧΞϓϦʮ&JHIUʯ ΤϯδχΞืूத 4BOTBO࠾༻ ݕࡧ SFDSVJU!TBOTBODPN·Ͱ ͓ؾܰʹ͝࿈བྷ͍ͩ͘͞ɻ ڵຯͷ͋Δํ͸

Slide 42

Slide 42 text

Copyright © Sansan, Inc. All rights reserved. > Thank you 42