Upgrade to Pro — share decks privately, control downloads, hide ads and more …

KotlinでDSL

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

 KotlinでDSL

Avatar for Jumpei Yamamoto

Jumpei Yamamoto

December 13, 2016
Tweet

More Decks by Jumpei Yamamoto

Other Decks in Programming

Transcript

  1. Copyright © Sansan, Inc. All rights reserved. > KotlinでDSL Jumpei

    Yamamoto 2016.12.13 第4回 Kotlin勉強会 in Sansan #kotlin_sansan
  2. Copyright © Sansan, Inc. All rights reserved. > ⾃⼰紹介 -

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

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

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

    テーマ:KotlinでDSL - DSLとは - KotlinでDSLの実践
  6. Copyright © Sansan, Inc. All rights reserved. > DSL ドメイン固有⾔語

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

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

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

    DSLを作る上でのポイント - 適切にドメインの語彙を使⽤しているか? - 読み⼿が(余計な複雑さなしに)いかに⾃然に⾔語を読み 下せるか? - 型付けによって以下に適切にIDEの補完を効かせることが できるか?
  10. 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()等 ドメインとは無関係の記述が複雑 ()とか;とか冗⻑
  11. Copyright © Sansan, Inc. All rights reserved. > Kotlin 名前付き引数

    / デフォルト引数 class People( name:String, age:Int, hobby:String = “” ) val tom = People( age = 12, name = “tom” ) 引数に名前をつける ことで、読み⼿がど の値を設定している かが⼀⽬瞭然 設定する順番も変え られる optionalな項⽬ (hobby)は省略可
  12. 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として実装 状態の定義 イベント名 遷移先の状態 アクション
  13. Copyright © Sansan, Inc. All rights reserved. > DSLを実装する⼿順 -

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

    State Machine Compiler ) セマンティックモデル StateMachine State State State … Event Event … Event
  15. Copyright © Sansan, Inc. All rights reserved. > Kotlinでモデル構造の定義 class

    StateMachine( var currentState: State, val states: Map<String, State>) class State( val name: String, val events: Map<String, Event>) class Event( val nextState: String, val action: (()-> Unit)?)

  16. 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で表現してみる
  17. 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 { } } } 状態の定義 イベント名 遷移先の状態 アクション
  18. 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 { } } } この構⽂がどのように実装されるか解説していきます。
  19. Copyright © Sansan, Inc. All rights reserved. > SMCを実装するクラス構成 StateMachineBuilder

    (内部DSLの実装) StateBuilder (内部DSLの実装) EventBuilder (内部DSLの実装) 1 * 1 * StateMachine State Event 1 * 1 * build() build() build()
  20. 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にて他のクラスの拡張関数を定義し ている場合にその拡張関数のスコープを限定することがで きる
  21. Copyright © Sansan, Inc. All rights reserved. > StateMachineBuilderの実装 class

    StateMachineBuilder { private val states = mutableMapOf<String, StateBuilder>() private var initialStateName:String? = null operator fun String.invoke(define: StateBuilder.() -> Unit){ 
 states[this] = StateBuilder(this).apply(define) 
 if(initialStateName == null){ 
 initialStateName = this } }
 
 }

  22. 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
  23. 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
  24. Copyright © Sansan, Inc. All rights reserved. > invoke operator

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

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

    StateMachineBuilder { private val states = mutableMapOf<String, StateBuilder>() 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 Ϩγʔόʔࢦఆͷؔ਺ܕ
  27. 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 { } } }
  28. 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
 } }

  29. Copyright © Sansan, Inc. All rights reserved. > 中置関数 class

    A { infix fun shows(arg:String){ print(arg) } } val a = A() a.shows(“bar”) a shows “bar”
  30. Copyright © Sansan, Inc. All rights reserved. > StateBuilderの実装 class

    StateBuilder(val name:String) { private val events = mutableMapOf<String, EventBuilder>() 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型の拡張関数
  31. 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 { } } }
  32. 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) } ラムダ式を引数にとる中置関数
  33. 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 中置関数は左結合!
  34. 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 { } } }
  35. 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
  36. $PQZSJHIU˜4BOTBO *OD"MMSJHIUTSFTFSWFE  4BOTBO͸Ұॹʹ৽͍͠Ձ஋Λ࡞͍ͬͯ͘ ஥ؒΛ͕͍ͯ͞͠·͢ɻ 3VCZ 3VCZPO3BJMT ʢ8FCΞϓϦέʔγϣϯʣ $ɼ"41/&5.7$ ʢ8FCΞϓϦέʔγϣϯʣ

    J04"OESPJEΞϓϦ   ݸਓ޲໊͚ࢗ؅ཧΞϓϦʮ&JHIUʯ   ໊ࢗσʔλԽ෼ࢄॲཧγεςϜ   ๏ਓ޲໊͚ࢗ؅ཧαʔϏεʮ4BOTBOʯ   ๏ਓ޲໊͚ࢗ؅ཧαʔϏε ʮ4BOTBOʯ   ݸਓ޲໊͚ࢗ؅ཧΞϓϦʮ&JHIUʯ ΤϯδχΞืूத 4BOTBO࠾༻ ݕࡧ SFDSVJU!TBOTBODPN·Ͱ ͓ؾܰʹ͝࿈བྷ͍ͩ͘͞ɻ ڵຯͷ͋Δํ͸