Skip to content

Commit

Permalink
chore(doc): update inline-functions to idea theme
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderPrendota committed Jul 13, 2018
1 parent cc6b2a9 commit c20f91e
Showing 1 changed file with 49 additions and 8 deletions.
57 changes: 49 additions & 8 deletions pages/docs/reference/inline-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ But it appears that in many cases this kind of overhead can be eliminated by inl
The functions shown below are good examples of this situation. I.e., the `lock()` function could be easily inlined at call-sites.
Consider the following case:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
lock(l) { foo() }
```
</div>

Instead of creating a function object for the parameter and generating a call, the compiler could emit the following code:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
l.lock()
try {
Expand All @@ -30,16 +33,17 @@ finally {
l.unlock()
}
```
</div>

Isn't it what we wanted from the very beginning?

To make the compiler do this, we need to mark the `lock()` function with the `inline` modifier:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
inline fun <T> lock(lock: Lock, body: () -> T): T {
// ...
}
inline fun <T> lock(lock: Lock, body: () -> T): T { ... }
```
</div>

The `inline` modifier affects both the function itself and the lambdas passed to it: all of those will be inlined
into the call site.
Expand All @@ -51,11 +55,11 @@ Inlining may cause the generated code to grow; however, if we do it in a reasona
In case you want only some of the lambdas passed to an inline function to be inlined, you can mark some of your function
parameters with the `noinline` modifier:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ...
}
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }
```
</div>

Inlinable lambdas can only be called inside the inline functions or passed as inlinable arguments,
but `noinline` ones can be manipulated in any way we like: stored in fields, passed around etc.
Expand All @@ -70,27 +74,48 @@ In Kotlin, we can only use a normal, unqualified `return` to exit a named functi
This means that to exit a lambda, we have to use a [label](returns.html#return-at-labels), and a bare `return` is forbidden
inside a lambda, because a lambda can not make the enclosing function return:

<div class="sample" markdown="1" theme="idea">
``` kotlin
fun ordinaryFunction(block: () -> Unit) {
println("hi!")
}
//sampleStart
fun foo() {
ordinaryFunction {
return // ERROR: can not make `foo` return here
}
}
//sampleEnd
fun main(args: Array<String>) {
foo()
}
```
</div>

But if the function the lambda is passed to is inlined, the return can be inlined as well, so it is allowed:

<div class="sample" markdown="1" theme="idea">
inline fun inlined(block: () -> Unit) {
println("hi!")
}
``` kotlin
//sampleStart
fun foo() {
inlineFunction {
inlined {
return // OK: the lambda is inlined
}
}
//sampleEnd
fun main(args: Array<String>) {
foo()
}
```
</div>

Such returns (located in a lambda, but exiting the enclosing function) are called *non-local* returns. We are used to
this sort of construct in loops, which inline functions often enclose:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
fun hasZeros(ints: List<Int>): Boolean {
ints.forEach {
Expand All @@ -99,12 +124,14 @@ fun hasZeros(ints: List<Int>): Boolean {
return false
}
```
</div>

Note that some inline functions may call the lambdas passed to them as parameters not directly from the function body,
but from another execution context, such as a local object or a nested function. In such cases, non-local control flow
is also not allowed in the lambdas. To indicate that, the lambda parameter needs to be marked with
the `crossinline` modifier:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
Expand All @@ -113,14 +140,15 @@ inline fun f(crossinline body: () -> Unit) {
// ...
}
```

</div>

> `break` and `continue` are not yet available in inlined lambdas, but we are planning to support them too.
## Reified type parameters

Sometimes we need to access a type passed to us as a parameter:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
Expand All @@ -131,22 +159,28 @@ fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
return p as T?
}
```
</div>

Here, we walk up a tree and use reflection to check if a node has a certain type.
It’s all fine, but the call site is not very pretty:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
treeNode.findParentOfType(MyTreeNode::class.java)
```
</div>

What we actually want is simply pass a type to this function, i.e. call it like this:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
treeNode.findParentOfType<MyTreeNode>()
```
</div>

To enable this, inline functions support *reified type parameters*, so we can write something like this:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
Expand All @@ -156,20 +190,23 @@ inline fun <reified T> TreeNode.findParentOfType(): T? {
return p as T?
}
```
</div>

We qualified the type parameter with the `reified` modifier, now it’s accessible inside the function,
almost as if it were a normal class. Since the function is inlined, no reflection is needed, normal operators like `!is`
and `as` are working now. Also, we can call it as mentioned above: `myTree.findParentOfType<MyTreeNodeType>()`.

Though reflection may not be needed in many cases, we can still use it with a reified type parameter:

<div class="sample" markdown="1" theme="idea" data-highlight-only>
``` kotlin
inline fun <reified T> membersOf() = T::class.members

fun main(s: Array<String>) {
println(membersOf<StringBuilder>().joinToString("\n"))
}
```
</div>

Normal functions (not marked as inline) can not have reified parameters.
A type that does not have a run-time representation (e.g. a non-reified type parameter or a fictitious type like `Nothing`)
Expand All @@ -184,6 +221,7 @@ For a low-level description, see the [spec document](https://github.com/JetBrain
The `inline` modifier can be used on accessors of properties that don't have a backing field.
You can annotate individual property accessors:

<div class="sample" markdown="1" theme="idea" data-highlight-only auto-indent="false">
``` kotlin
val foo: Foo
inline get() = Foo()
Expand All @@ -192,14 +230,17 @@ var bar: Bar
get() = ...
inline set(v) { ... }
```
</div>

You can also annotate an entire property, which marks both of its accessors as inline:

<div class="sample" markdown="1" theme="idea" data-highlight-only auto-indent="false">
``` kotlin
inline var bar: Bar
get() = ...
set(v) { ... }
```
</div>

At the call site, inline accessors are inlined as regular inline functions.

Expand Down

0 comments on commit c20f91e

Please sign in to comment.