About me
@_a_akira AAkira
CyberAgent, Inc.
Akira Aratani
https://aakira.app
!"#$%&'()*+,-./012345
Slide 3
Slide 3 text
No content
Slide 4
Slide 4 text
New Type Inference and Related Language Features
Svetlana Isakova
Svetlana Isakova has worked on the Kotlin language at
JetBrains. She’s co-authored the book Kotlin in Action
and now is a Developer Advocate, where she spends her
time teaching Kotlin and speaking at conferences
worldwide.
Slide 5
Slide 5 text
Agenda
• Experimental features
• Contracts
• New type Inference
Slide 6
Slide 6 text
λΠτϧ
Experimental features
Slide 7
Slide 7 text
Experimental features
Purpose
To let new features be tried by early adopters
as soon as possible
Slide 8
Slide 8 text
Experimental features
Tools > Kotlin > Enable migrations detection (experimental)
Experimental features
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.*
ˣ
1.2
1.3
Slide 12
Slide 12 text
Experimental features
自分でExperimental APIを作ることも可能
@Experimental
annotation class ShinyNewAPI
Slide 13
Slide 13 text
Experimental features
// Ϋϥεʹ͚ΒΕΔ
@ShinyNewAPI
class Foo {}
// ؔʹ͚ΒΕΔ
@ShinyNewAPI
fun Foo.bar() = ""
Slide 14
Slide 14 text
Experimental features
@ShinyNewAPI
fun doSomething() {
val foo = Foo()
foo.bar()
}
@ShinyNewAPI
fun main() {
doSomething()
}
Slide 15
Slide 15 text
Experimental features
@ShinyNewAPI
fun doSomething() {
val foo = Foo()
foo.bar()
}
@ShinyNewAPI
fun main() {
doSomething()
}
1.3からmain関数の引数はオプションになった
Slide 16
Slide 16 text
Experimental features
@ShinyNewAPI
fun doSomething() {
val foo = Foo()
foo.bar()
}
@ShinyNewAPI
fun main() {
doSomething()
}
Slide 17
Slide 17 text
Experimental features
@UseExperimental(ShinyNewAPI::class)
fun doSomething() {
val foo = Foo()
foo.bar()
}
fun main() {
doSomething()
}
Slide 18
Slide 18 text
Experimental features
@UseExperimental(ShinyNewAPI::class)
fun doSomething() {
val foo = Foo()
foo.bar()
}
fun main() {
doSomething()
}
Slide 19
Slide 19 text
$POUSBDUT
Contracts
Slide 20
Slide 20 text
Kotlin Puzzler
Kotlin1.2で実行する場合
fun main(args: Array) {
var hoge: Int?
run {
hoge = 46
}
println(hoge)
}
// a) 46
// b) NullPointerException
// c) null
// d) Will not compile
Slide 21
Slide 21 text
Kotlin Puzzler
Kotlin 1.3より前では実行出来ない
fun main(args: Array) {
var hoge: Int?
run {
hoge = 46
}
println(hoge)
}
// a) 46
// b) NullPointerException
// c) null
// d) Will not compile
Slide 22
Slide 22 text
Kotlin Puzzler
fun main(args: Array) {
var hoge: Int?
run {
hoge = 46
}
println(hoge)
}
// a) 46
// b) NullPointerException
// c) null
// d) Will not compile
コンパイラは初期化されているのか、判断出来ない
printの部分で “variable hoge must be initialized”
Slide 23
Slide 23 text
Contracts
// 1.3
inline fun run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
// 1.2
public inline fun run(block: () -> R): R = block()
Slide 24
Slide 24 text
Contracts
// 1.3
inline fun run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
// 1.2
public inline fun run(block: () -> R): R = block()
Slide 25
Slide 25 text
Contracts
• can be implicitly changed
• can implicitly break code depending on it
なぜコンパイラはrun内で代入されたhogeがnon-nullなのを理解出来ないのか
Slide 26
Slide 26 text
Contracts
// 1.3
inline fun run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
// 1.2
public inline fun run(block: () -> R): R = block()
コンパイラに一度だけ実行されるという情報を
教えてあげる
Slide 27
Slide 27 text
Contracts
Kotlin 1.3では実行可能
fun main(args: Array) {
var hoge: Int?
run {
hoge = 46
}
println(hoge)
}
Slide 28
Slide 28 text
Contracts
Kotlin 1.3より前では実行出来ない
fun main(args: Array) {
val s : String? = ""
if(!s.isNullOrEmpty()) {
s.first()
}
}
Slide 29
Slide 29 text
Contracts
Kotlin 1.3より前では実行出来ない
fun main(args: Array) {
val s : String? = ""
if(!s.isNullOrEmpty()) {
s.first()
}
}
sがnot nullだとはコンパイラは理解出来ない
Slide 30
Slide 30 text
Contracts
1.3でのisNullOrEmptyの実装
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}
return this == null || this.length == 0
}
New type inference
kotlin {
experimental { newInference 'enable' }
}
Slide 41
Slide 41 text
New type inference
• SAM conversions for Kotlin functions
• Better inference for builders
• Better inference for call chains
• Better inference for intersection types
Slide 42
Slide 42 text
public interface Action {
void execute(T target);
}
SAM conversions
.java
fun handleInput(handler: Action) {
...
}
fun main() {
handleInput(Action { target ->
println(target)
})
}
.kt
Slide 43
Slide 43 text
public interface Action {
void execute(T target);
}
SAM conversions
.java
fun handleInput(handler: Action) {
...
}
fun main() {
handleInput { println(it) }
}
.kt
1.3からはこう書ける
Slide 44
Slide 44 text
SAM conversions
val obs = Observable.just(100)
Observable.just(1)
.zipWith(obs, BiFunction { a, b -> a + b })
.subscribe { }
RxJava2から第二引数でlamda式が使えなくなっていた
=> RxKotlin(https://github.com/ReactiveX/RxKotlin) で回避していた
Slide 45
Slide 45 text
SAM conversions
val obs = Observable.just(100)
Observable.just(1)
.zipWith(obs) { a, b -> a + b }
.subscribe { }
RxKotlinを使わなくても良くなった!
Slide 46
Slide 46 text
Inference for builder lambdas
val seq = sequence {
yield(42)
print("hoge")
yield(4)
}
Kotlin1.3から型パラメータが不要
Slide 47
Slide 47 text
Inference for builder lambdas
Kotlin1.3から型パラメータが不要
val seq = sequence {
yield(42)
print("hoge")
yield(4)
}
Slide 48
Slide 48 text
Inference for builder lambdas
fun buildList(
init: MutableList.() -> Unit
): List {
return mutableListOf().apply(init)
}
fun main() {
val list = buildList {
add(1)
add(2)
}
}
もちろん自分でも定義可能
Slide 49
Slide 49 text
Inference for builder lambdas
@UseExperimental(ExperimentalTypeInference::class)
fun buildList(
@BuilderInference init: MutableList.() -> Unit
): List {
return mutableListOf().apply(init)
}
fun main() {
val list = buildList {
add(1)
add(2)
}
}
BuilderInference annotationを使う
Slide 50
Slide 50 text
Inference for builder lambdas
@UseExperimental(ExperimentalTypeInference::class)
fun buildList(
@BuilderInference init: MutableList.() -> Unit
): List {
return mutableListOf().apply(init)
}
fun main() {
val list = buildList {
add(1)
add(2)
}
}
BuilderInference annotationを使う
Slide 51
Slide 51 text
Inference for builder lambdas
@UseExperimental(ExperimentalTypeInference::class)
fun buildList(
@BuilderInference init: MutableList.() -> Unit
): List {
return mutableListOf().apply(init)
}
fun main() {
val list = buildList {
add(1)
add(2)
}
}
これによって1.3より前で
Slide 52
Slide 52 text
Inference for builder lambdas
@UseExperimental(ExperimentalTypeInference::class)
fun buildList(
@BuilderInference init: MutableList.() -> Unit
): List {
return mutableListOf().apply(init)
}
fun main() {
val list = buildList {
add(1)
add(2)
}
}
Intを省略可能
Slide 53
Slide 53 text
Inference for call chains
fun createMap(): Map = MapBuilder()
.put("answer", 42)
.build()
MapBuilderは自分で定義した関数? (発表資料ままです)
Kotlin 1.2: One call is analyzed at a time
Kotlin 1.3: A call chain is analyzed
Slide 54
Slide 54 text
Inference for call chains
fun createMap() = MapBuilder()
.put("answer", 42)
.build()
Kotlin1.3からは全体で評価されるので型定義を省略可能
Slide 55
Slide 55 text
Intersection types
interface Foo
interface Bar
fun hoge(thing: T) where T : Foo, T : Bar {
println(thing)
}
fun better() {
val something: Better = Better.A
if (something is Foo && something is Bar) {
hoge(something) // Foo & Bar
}
}
sealed class Better {
object A : Better()
object B : Better(), Foo
object C : Better(), Foo, Bar
}
Slide 56
Slide 56 text
Intersection types
interface Foo
interface Bar
fun hoge(thing: T) where T : Foo, T : Bar {
println(thing)
}
fun better() {
val something: Better = Better.A
if (something is Foo && something is Bar) {
hoge(something) // Foo & Bar
}
}
sealed class Better {
object A : Better()
object B : Better(), Foo
object C : Better(), Foo, Bar
}
Slide 57
Slide 57 text
Intersection types
interface Foo
interface Bar
fun hoge(thing: T) where T : Foo, T : Bar {
println(thing)
}
fun better() {
val something: Better = Better.A
if (something is Foo && something is Bar) {
hoge(something) // Foo & Bar
}
}
sealed class Better {
object A : Better()
object B : Better(), Foo
object C : Better(), Foo, Bar
}
somethingは Foo & Barと判定しているが…
Slide 58
Slide 58 text
Intersection types
interface Foo
interface Bar
fun hoge(thing: T) where T : Foo, T : Bar {
println(thing)
}
fun better() {
val something: Better = Better.A
if (something is Foo && something is Bar) {
hoge(something) // Compile error
}
}
sealed class Better {
object A : Better()
object B : Better(), Foo
object C : Better(), Foo, Bar
}
Kotlin1.2まではコンパイルエラーだった
Slide 59
Slide 59 text
Intersection types
interface Foo
interface Bar
fun hoge(thing: T) where T : Foo, T : Bar {
println(thing)
}
fun better() {
val something: Better = Better.A
if (something is Foo && something is Bar) {
hoge(something) // Foo & Bar
}
}
sealed class Better {
object A : Better()
object B : Better(), Foo
object C : Better(), Foo, Bar
}
1.3からはコンパイル可