new type inference related language features
play

New Type Inference & Related Language Features Svetlana Isakova - PowerPoint PPT Presentation

New Type Inference & Related Language Features Svetlana Isakova @sveta_isakova Agenda Experimental features Contracts New type inference Experimental features Experimental features Our goal: to let new features be tried by early


  1. New Type Inference & Related Language Features Svetlana Isakova @sveta_isakova

  2. Agenda Experimental features Contracts New type inference

  3. Experimental features

  4. Experimental features Our goal: to let new features be tried by early adopters as soon as possible

  5. Example: Coroutines Kotlin 1.2 import kotlinx.coroutines.experimental.* might be stable enough, but no backward compatibility guarantees Kotlin 1.3 import kotlinx.coroutines.* backward compatibility guarantees

  6. Automatic migration

  7. Experimental Language Features • you need to explicitly opt in at the call site to use experimental features kotlin { experimental { coroutines 'enable' } }

  8. Experimental API for Libraries • can be publicly released as a part of the library • may break at any moment

  9. Experimental API You can mark your shiny new class or function as experimental @Experimental annotation class ShinyNewAPI @ShinyNewAPI class Foo { ... }

  10. Using experimental API @UseExperimental(ShinyNewAPI:: class ) fun doSomethingImportant() { val foo = Foo() ... }

  11. Experimental: Summary • feedback loop for new features and API

  12. Contracts

  13. Changes in standard library

  14. Changes in standard library inline fun <R> run(block: () -> R): R = block()

  15. Changes in standard library inline fun <R> run(block: () -> R): R = block() inline fun <R> run(block: () -> R): R { contract { callsInPlace(block, InvocationKind. EXACTLY_ONCE ) } return block() }

  16. Changes in standard library inline fun <R> run(block: () -> R): R = block() inline fun <R> run(block: () -> R): R { contract { callsInPlace(block, InvocationKind. EXACTLY_ONCE ) } return block() }

  17. We know something about run , which the compiler doesn’t val answer: Int run { answer = 42 } println (answer)

  18. We know something about run , which the compiler doesn’t val answer: Int run { answer = 42 } Compiler error: Captured values initialization is forbidden due to possible reassignment println (answer)

  19. We know something about isNullOrEmpty , which the compiler doesn’t val s: String? = "" if (!s. isNullOrEmpty ()) { s. first () }

  20. We know something about isNullOrEmpty , which the compiler doesn’t val s: String? = "" if (!s. isNullOrEmpty ()) { s. first () } Compiler error: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

  21. Kotlin Contracts …allow to share extra information about code semantics with the compiler

  22. Variable initialization inside run val answer: Int run { answer = 42 } println (answer)

  23. Variable initialization inside run val answer: Int run { ✓ answer = 42 } println (answer)

  24. Making smart casts even smarter val s: String? = "" if (!s. isNullOrEmpty ()) { s. first () }

  25. Making smart casts even smarter val s: String? = "" if (!s. isNullOrEmpty ()) { ✓ s. first () }

  26. Contract: block lambda will be always called once inline fun <R> run(block: () -> R): R { contract { callsInPlace(block, InvocationKind. EXACTLY_ONCE ) } return block() }

  27. Contract for calling inlined lambda in-place run, let, with, apply, also takeIf, takeUnless, synchronized

  28. Contract: if the function returns false , the receiver is not-null fun String?.isNullOrEmpty(): Boolean { contract { returns( false ) implies ( this @isNullOrEmpty != null ) } return this == null || this . length == 0 }

  29. Contract: if the function returns false , the receiver is not-null fun String?.isNullOrEmpty(): Boolean { contract { returns( false ) implies ( this @isNullOrEmpty != null ) } return this == null || this . length == 0 } val s: String? = "" if (!s. isNullOrEmpty ()) { ✓ s. first () }

  30. Contract: if the function returns false , the receiver is not-null fun String?.isNullOrEmpty(): Boolean { contract { returns( false ) implies ( this @isNullOrEmpty != null ) } return this == null || this . length == 0 }

  31. Contract: if the function returns false , the receiver is not-null fun String?.isNullOrEmpty(): Boolean { contract { this: ContractBuilder returns( false ) implies ( this @isNullOrEmpty != null ) } return this == null || this . length == 0 }

  32. Contract: if the function returns a given value, a condition is satisfied isNullOrEmpty, isNullOrBlank kotlin.test: assertTrue, assertFalse, assertNotNull check, checkNotNull, require, requireNotNull

  33. Kotlin Contract experimental extra information by developer & compiler uses this information for code analysis

  34. Kotlin Contract extra information by developer & compiler uses this information for code analysis & to be supported checking that the information is correct at compile time or runtime

  35. Why can’t compiler just implicitly infer such information?

  36. Why can’t compiler just implicitly infer such information? Because then such implicitly inferred information: - can be implicitly changed - can implicitly break code depending on it

  37. Why can’t compiler just implicitly infer such information? Because then such implicitly inferred information: - can be implicitly changed - can implicitly break code depending on it Contract = explicit statement about function behaviour

  38. Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null ) { if (actual == null ) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull (input) assertTrue(input!!. any { it . isDigit () } ) }

  39. Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null ) { if (actual == null ) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull (input) assertTrue(input!!. any { it . isDigit () } ) }

  40. Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null ) { contract { returns() implies (actual != null ) } if (actual == null ) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull (input) assertTrue (input. all { it . isDigit () } ) }

  41. Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null ) { contract { returns() implies (actual != null ) } Experimental if (actual == null ) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull (input) assertTrue (input. all { it . isDigit () } ) }

  42. Using contracts for your own functions fun assertNotNull(actual: Any?, message: String? = null ) { contract { returns() implies (actual != null ) } Experimental if (actual == null ) { throw AssertionError( message ?: "Value must not be null" ) } } fun testInput(input: String?) { assertNotNull (input) assertTrue (input. all { it . isDigit () } ) }

  43. Contracts: Summary • handy functions ( run , isEmptyOrNull ) are even more useful • contract DSL will change • you can go and try it out

  44. New type inference

  45. New type inference better and more powerful type inference new features are supported

  46. Might be turned on in Kotlin 1.3 kotlin { experimental { newInference 'enable' } }

  47. Kotlin libraries • Libraries should specify return types for public API • Overloaded functions must do the same thing

  48. Kotlin libraries • Libraries should specify return types for public API turn on an IDE inspection “Public API declaration has implicit return type” • Overloaded functions must do the same thing

  49. New type inference • SAM conversions for Kotlin functions • better inference for builders • better inference for call chains • better inference for intersection types

  50. SAM conversions for Kotlin functions

  51. SAM conversions for Kotlin functions public interface Action<T> { void execute(T target); } fun handleInput(handler: Action<String>) { ... }

  52. SAM conversions for Kotlin functions public interface Action<T> { void execute(T target); } fun handleInput(handler: Action<String>) { ... } You can pass a lambda as an argument when a Java SAM-interface is expected: handleInput { println (it) }

  53. Support for several SAM arguments Old inference: observable.zipWith(anotherObservable, BiFunction { x, y -> x + y } )

  54. Support for several SAM arguments Old inference: observable.zipWith(anotherObservable, BiFunction { x, y -> x + y } ) class Observable { public final Observable zipWith( ObservableSource other, BiFunction zipper) {…} } SAM interfaces

  55. Support for several SAM arguments Old inference: observable.zipWith(anotherObservable, BiFunction { x, y -> x + y } ) New inference: observable.zipWith(anotherObservable) { x, y -> x + y }

  56. Support for several SAM arguments Old inference: observable.zipWith(anotherObservable, BiFunction { x, y -> x + y } ) New inference: observable.zipWith(anotherObservable) { x, y -> x + y } ✓

  57. Builder inference

  58. Inference for sequence val seq = sequence { yield(42) }

  59. Inference for sequence : Sequence<Int> val seq = sequence { yield(42) }

Recommend


More recommend