9. Command
• State State
• processed sequentially
• low-level construct; avoid making custom commands
逐次処理される
低レベルなものなので、カスタムコマンドは避ける
10. examples of command
commands
help, tasks
projects, project hello
set name := "foo"
<command1>; <command2>
++ 2.13.0, ++ 2.13.0!, +<command>
shell command
act command
11. act command
• lifts tasks and settings into a command
act コマンドはタスクやセッティングを持ち上げる
12. which state is it changing?
commands changes
help, tasks no changes
projects, project hello build structure
set name := "foo" build structure
<command1>; <command2> both
++ 2.13.0, ++ 2.13.0!, +<command> both
act command disk
変更している状態はどっち?
13. why State + Command?
何のために State + Command があるの?
14. why State + Command?
• predictable checkpoint
• building block for interactiveness:
"automates repeatable tasks"
予測可能なチェックポイント
インタラクティブ性のための構成要素
21. distributed events
• a → b (a happens before b)
• a ↛ b (a does not happen before b)
• 2 distinct events a and b are said to
be concurrent if a ↛ b and b ↛ a
a ↛ b かつ b ↛ a であるとき、2つのイベントは並行
22. distributed events
• a → b (a happens before b)
• a ↛ b (a does not happen before b)
• 2 distinct events a and b are said to
be concurrent if a ↛ b and b ↛ a
2 events are concurrent if neither can causality affect
each other
2つのイベントがお互いに因果的に影響を持たない場合、並行
23. Applicative functor
scala> (3.some, 2.some) mapN { _ + _ }
res8: Option[Int] = Some(5)
scala> (none[Int], 5.some) mapN { _ - _ }
res9: Option[Int] = None
See 'Oh, All the things you'll traverse' by Luka Jacobowitz
24. for comprehension
getLine flatMap { x =>
print(length(x)) flatMap { _ =>
getLine flatMap { y =>
IO(x ++ y)
}
}
}
for {
x <- getLine
_ <- print(length(x))
y <- getLine
} yield x ++ y
def procedural: Unit = {
val x = getLine
print(length(x))
val y = getLine
x ++ y
}
for 内包表記
25. build.sbt DSL
// sbt 0.12 style
foo <<= (compile in Compile, bar) map { (c, b) =>
doSomething(b)
}
// sbt 1.x style
foo := {
val c = (Compile / compile).value
val b = bar.value
doSomething(b)
}
26. build.sbt DSL
// sbt 0.12 style
foo <<= (compile in Compile, bar) map { (c, b) =>
doSomething(b)
}
// sbt 1.x style
foo := {
val c = (Compile / compile).value
val b = bar.value
doSomething(b)
}
Applicative composition
27. build.sbt DSL
foo := {
val c = (Compile / compile).value
val b = bar.value
doSomething(b)
}
Compile / compile bar
foo
"happens before"
28. build.sbt DSL
foo := {
val c = (Compile / compile).value
val b = bar.value
doSomething(b)
}
Compile / compile bar
foo
"happens before"
line of sand in time-space
29. Applicative composition
foo := {
val c = (Compile / compile).value
val b = bar.value
doSomething(b)
}
Test / test := {
val c = (Compile / compile).value
val f = foo.value
}
Compile / compile bar
foo
"happens before"
Test / test
"happens before"
31. why Applicative composition?
Compile / compile bar
foo
"happens before"
Test / test
"happens before"
1. minimality (executes task at most once, for input
that changed)
evaluated only once
ミニマル性 (タスクは入力が変化したとき、最多で1回のみ)
Compile / compile は一回のみ実行される
32. why Applicative composition?
Compile / compile bar
foo
"happens before"
Test / test
"happens before"
1. minimality
2. automatic parallel processing
自動並列処理
33. act command
• given a task, creates a plan that evaluates the task
in current and aggregated subprojects
• concurrent tasks are evaluated in parallel
lazy val root = (project in file("."))
.aggregate(util, core)
.settings(...)
lazy val util = (project in file("util"))
.dependsOn(core)
lazy val core = (project in file("core"))
act コマンドは、与えられたタスクを現在サブプロジェクトと
集約されたサブプロジェクトで実行するプランを作成する
34. act command
• how does it relate with State?
s0 s1 s2
reload (settings) act (task) act (task)
状態との関連は?
35. tasks vs commands
• prefer tasks for plugin extension
• tasks compose automatically
• command composition is stateful / sequential
プラグイン拡張性にはタスクがオススメ
タスクは自動合成するが、コマンド合成は stateful
45. key-value store
key value
name
foo/name
foo/Zero/Zero/name helloworld
foo/Compile/console/scalacOptions List()
• keys are automatically scoped to the current subproject
• other scope axes default to Zero
• Global = Zero/Zero/Zero
キーは自動的にカレント・サブプロジェクトにスコープされる
他のスコープ軸のデフォルトは Zero
46. why key-value store?
• inspect command
• provides flexible extensibility on most aspects of the build
• plugins can created based on other plugins
sbt:sbtRoot> inspect tree test
[info] Test / test = Task[Unit]
[info] +-Test / executeTests = Task[sbt.Tests$Output]
[info] | +-classLoaderLayeringStrategy = ScalaLibrary
[info] | +-Test / loadedTestFrameworks = Task[scala.collection.immutable.Map[sbt.TestFramework, ..
[info] | | +-Test / loadedTestFrameworks / streams = Task[sbt.std.TaskStreams[sbt.internal.util...
[info] | | | +-Global / streamsManager = Task[sbt.std.Streams[sbt.internal.util.Init$..
[info] | | |
構造を inspect することができる
ビルドを形成する多くの部分を柔軟に拡張できる
47. setting expresion
name := { "hello" }
key operator (setting/task) body
• operators :=, +=, ++=
• a setting expression represents a transformation of
k-v store
セッティング式
48. setting expresion
ThisBuild / organization := "com.example"
name := (ThisBuild / organization).value + "12"
• use .value to lookup the setting/task value
• key-value store forms a DAG (directed acyclic
graph)
.value を使ってセッティングやタスクの値を参照する
key-value ストアは DAG を形成する
49. delegation rules
x := (core / Test / console / scalacOption).value
1. look for the specified key, then try in-task ⇢ Zero
2. next try Test ⇢ Runtime ⇢ Compile ⇢ Zero
3. next try core ⇢ ThisBuild ⇢ Zero
4. precedence: subproject > configuration > in-task
5. transitive evaluation doesn't carry original context (no
dynamic dispatch)
5つの移譲ルールがあり、指定されたキーが無い場合に
次に見る場所を規定する
50. delegation rules
ThisBuild / version := name.value
lazy val b = project
.settings(
name := "b"
something := version.value
)
• transitive evaluation doesn't carry original context
(no dynamic dispatch)
間接的評価は元のコンテキストを伴わない
OO の this.draw 的な振る舞いでは無い
51. tip for plugin authors
• define custom keys at the widest scope (Global),
and reference the keys using the narrowest scope
Global / obfuscateLogic := Logic.Default
Compile / obfuscate := {
val logic = (Compile / obfuscateLogic).value
doObfuscate(logic)
}
• This allows build users various levels to rewire the
setting (Global, ThisBuild, proj, proj/Compile)
カスタムキーは最も広いスコープ付けで定義し、
最も狭いスコープ付けで参照すると最大の柔軟性を得られる