Share
Embed
Transcript
42 slides
-
Copyright © Sansan, Inc. All rights reserved. > KotlinでDSL Jumpei
Yamamoto 2016.12.13 第4回 Kotlin勉強会 in Sansan #kotlin_sansan -
Copyright © Sansan, Inc. All rights reserved. > ⾃⼰紹介 -
⼭本純平 - Sansan株式会社 Eight事業部 - EightのAndroidアプリの開発 - twitter: @boohbah - github: https://github.com/yamamotoj -
Copyright © Sansan, Inc. All rights reserved. > Pikkel •
AndroidでIcePickのように状態の保存ができるKotlinの ライブラリ • https://github.com/yamamotoj/Pikkel -
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) } } -
Copyright © Sansan, Inc. All rights reserved. > Agenda -
テーマ:KotlinでDSL - DSLとは - KotlinでDSLの実践 -
Copyright © Sansan, Inc. All rights reserved. > DSL ドメイン固有⾔語
- Domain Specific Language - “特定のドメインに集中し、限定された表現⼒ を備えたコンピュータプログラミング⾔語” by 「ドメイン特化⾔語」マーチン・ファウラー著 - ⼤きく分けて外部DSLと内部DSLがある -
Copyright © Sansan, Inc. All rights reserved. > 外部DSL -
動作するアプリケーションとは別の独⽴した⾔語 - 独⾃に構⽂解析、字句解析を⾏い、必要な機能を実装す る。 - 基本的に独⾃の構⽂を持つが、XML等の他の⾔語の構⽂を 利⽤することもある。 CSSの例 -
Copyright © Sansan, Inc. All rights reserved. > 内部DSLとは -
アプリケーションの⾔語を 特定のドメイン向けにそれっ ぽく使う。 - 表現能⼒ (== 読みやすさ)はホスト⾔語の制約に依存 - 「流れるようなインターフェイス」⊂ 内部DSL - 動的型付⾔語(Rubyとか)と静的型付⾔語(Javaとか Kotlinとか)で実装の⽅法が異なる -
Copyright © Sansan, Inc. All rights reserved. > 内部DSLの例 -
Ruby on rails, rake, gradle - Anko, -
Copyright © Sansan, Inc. All rights reserved. > 静的型付け⾔語で >
DSLを作る上でのポイント - 適切にドメインの語彙を使⽤しているか? - 読み⼿が(余計な複雑さなしに)いかに⾃然に⾔語を読み 下せるか? - 型付けによって以下に適切にIDEの補完を効かせることが できるか? -
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()等 ドメインとは無関係の記述が複雑 ()とか;とか冗⻑ -
Copyright © Sansan, Inc. All rights reserved. > Kotlin 名前付き引数
/ デフォルト引数 class People( name:String, age:Int, hobby:String = “” ) val tom = People( age = 12, name = “tom” ) 引数に名前をつける ことで、読み⼿がど の値を設定している かが⼀⽬瞭然 設定する順番も変え られる optionalな項⽬ (hobby)は省略可 -
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として実装 状態の定義 イベント名 遷移先の状態 アクション -
Copyright © Sansan, Inc. All rights reserved. > DSLを実装する⼿順 -
DSLによって実現したいモデル構造(セマンティックモデ ル)を定義する。 - どういう構⽂を定義するかを考える - 流れるようなインターフェイス? - 階層構造 - 構⽂の評価、改善を繰り返す -
Copyright © Sansan, Inc. All rights reserved. > SMC (
State Machine Compiler ) セマンティックモデル StateMachine State State State … Event Event … Event -
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)?) -
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で表現してみる -
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 { } } } 状態の定義 イベント名 遷移先の状態 アクション -
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 { } } } この構⽂がどのように実装されるか解説していきます。 -
Copyright © Sansan, Inc. All rights reserved. > SMCを実装するクラス構成 StateMachineBuilder
(内部DSLの実装) StateBuilder (内部DSLの実装) EventBuilder (内部DSLの実装) 1 * 1 * StateMachine State Event 1 * 1 * build() build() build() -
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にて他のクラスの拡張関数を定義し ている場合にその拡張関数のスコープを限定することがで きる -
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 } } } -
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 -
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 -
Copyright © Sansan, Inc. All rights reserved. > invoke operator
// ֦ுؔͱ࣮ͯ͠ operator fun String.invoke(){ print(“this is $this”) } “foo”() // this is foo -
Copyright © Sansan, Inc. All rights reserved. > invoke operator
// ϥϜμࣜΛҾʹͱΔ operator fun String.invoke(f: String -> Unit){ f(this) } “foo”{ print(it) } -
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 Ϩγʔόʔࢦఆͷؔܕ -
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 { } } } -
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 } } -
Copyright © Sansan, Inc. All rights reserved. > 中置関数 class
A { infix fun shows(arg:String){ print(arg) } } val a = A() a.shows(“bar”) a shows “bar” -
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型の拡張関数 -
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 { } } } -
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) } ラムダ式を引数にとる中置関数 -
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 中置関数は左結合! -
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 { } } } -
Copyright © Sansan, Inc. All rights reserved. > ソース公開しています。 >
https://github.com/yamamotoj/KotlinSMC -
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 -
$PQZSJHIU4BOTBO *OD"MMSJHIUTSFTFSWFE 4BOTBOҰॹʹ৽͍͠ՁΛ࡞͍ͬͯ͘ ؒΛ͕͍ͯ͞͠·͢ɻ 3VCZ 3VCZPO3BJMT ʢ8FCΞϓϦέʔγϣϯʣ $ɼ"41/&5.7$ ʢ8FCΞϓϦέʔγϣϯʣ
J04"OESPJEΞϓϦ ݸਓ໊͚ཧΞϓϦʮ&JHIUʯ ໊σʔλԽࢄॲཧγεςϜ ๏ਓ໊͚ཧαʔϏεʮ4BOTBOʯ ๏ਓ໊͚ཧαʔϏε ʮ4BOTBOʯ ݸਓ໊͚ཧΞϓϦʮ&JHIUʯ ΤϯδχΞืूத 4BOTBO࠾༻ ݕࡧ SFDSVJU!TBOTBODPN·Ͱ ͓ؾܰʹ͝࿈བྷ͍ͩ͘͞ɻ ڵຯͷ͋Δํ