-
-
Notifications
You must be signed in to change notification settings - Fork 204
Add Nullability concept #714
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
colinleach
wants to merge
4
commits into
exercism:main
Choose a base branch
from
colinleach:nullability
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "authors": [ | ||
| "colinleach" | ||
| ], | ||
| "contributors": [], | ||
| "blurb": "Kotlin provides optional nullable types, plus functionality to make nullability safe and convenient to use." | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| # About Nullability | ||
|
|
||
| In an ideal world, we could rely on everything being nicely defined, with concrete values and no gaps. | ||
|
|
||
| This conflicts with our everyday experience, and thus many programming languages have a way to represent the absence of a value: null or NULL, nil, None, nothing... | ||
|
|
||
| This was easy to introduce (back in 1965), but soon led to regrets. | ||
| The inventor later called it his ["Billion Dollar Mistake"][web-mistake]. | ||
|
|
||
| In recent decades, language designers have explored many ways to make nulls available but (mostly) safe. | ||
|
|
||
| Kotlin has a few [general principles][ref-null-safety], which [differ significantly from Java][ref-java2kotlin]. | ||
| _The differences mean that Kotlin has special functionality to deal with Java interop, though details are beyond the scope of this Concept._ | ||
|
|
||
| - Kotlin has a special `null` value. | ||
| - All types _default_ to being non-nullable. | ||
| - Most types can be _made_ nullable, but the programmer must explicitly order this. | ||
colinleach marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Functions which might fail with an exception are likely to have a variant with a nullable return value. | ||
| - Special, highly terse syntax tries to make testing for and responding to nulls as easy as possible. | ||
|
|
||
| ## Creating nullable variables | ||
|
|
||
| Standard types generally have a [nullable equivalent][ref-nullable-type], with a `?` suffix: for example `String?` instead of `String`. | ||
colinleach marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ```kotlin | ||
| var a = "Kotlin" // inferred type is String | ||
| a = null // => Error: Null can not be a value of a non-null type String | ||
|
|
||
| var b: String? = "Kotlin" // nullable type | ||
| b = null // => b is now null | ||
| b == null // => true | ||
| ``` | ||
|
|
||
| ## Useful operators and functions | ||
|
|
||
| ### The [safe call operator][ref-safe-call] `?.` | ||
|
|
||
| For a non-nullable `String`, we can get the `.length` property. | ||
|
|
||
| A nullable `String?` fails with the same syntax, but using `?.length` allows for the possibility of a `null`. | ||
| The return type is `Int?`. | ||
|
|
||
| ```kotlin | ||
| var a = "Kotlin" | ||
| a.length // => 6 | ||
|
|
||
| // b is still null | ||
| b.length // => Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String? | ||
| b?.length // => null | ||
| a?.length // => 6 | ||
| ``` | ||
|
|
||
| As we saw in the last line above, `?.` also works with non-nullable types. | ||
|
|
||
| ### The [Elvis operator][ref-elvis] `?:` | ||
|
|
||
| Extending the idea of the safe-call operator, the [Elvis operator][wiki-elvis] lets us supply a default value to replace `null`. | ||
|
|
||
| ```kotlin | ||
| // b is still null | ||
| b?.length ?: -1 // => -1, in place of null | ||
| a?.length ?: -1 // => 6, as before | ||
| ``` | ||
|
|
||
| ### [Not-null assertion operator][ref-not-null] `!!` | ||
|
|
||
| With this operator, a nullable type is forced to be treated as non-null, when the programmer is confident about this. | ||
| Perhaps we sometimes understand the program logic better than an over-cautious compiler? | ||
|
|
||
| ```kotlin | ||
| // b is still a String? | ||
| b = "Kotlin?" | ||
| b!!.length // => 7 | ||
| ``` | ||
|
|
||
| The responsibility is now on us: if `b` is still `null` we get a `NullPointerException`. | ||
|
|
||
| ### [Let function][ref-let] `?.let` | ||
|
|
||
| If passing a nullable item to a block of code, we could do specific null checks `if (item != null) { do something }`. | ||
|
|
||
| It is simpler and more idiomatic to use `item?.let { do something }`, | ||
| The `do something` will only execute when `item` is not null. | ||
|
|
||
| ### Functions of type `somethingOrNone()` | ||
|
|
||
| Some functions may fail, for example when asking for an invalid index in a string or list. | ||
|
|
||
| Rather than throwing an exception, we could use the [`...OrNull` variant][ref-elem-at-or-null] of the function, to get (in this case) an `Int?` return value. | ||
|
|
||
| ```kotlin | ||
| val str = "Kotlin" | ||
| str.elementAtOrNull(10) // => null | ||
| str.elementAt(10) // => StringIndexOutOfBoundsException: String index out of range: 10 | ||
| ``` | ||
|
|
||
| [ref-java2kotlin]: https://kotlinlang.org/docs/java-to-kotlin-nullability-guide.html | ||
| [web-mistake]: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/ | ||
| [ref-null-safety]: https://kotlinlang.org/docs/null-safety.html | ||
| [ref-elvis]: https://kotlinlang.org/docs/null-safety.html#elvis-operator | ||
| [wiki-elvis]: https://en.wikipedia.org/wiki/Elvis_operator | ||
| [ref-nullable-type]: https://kotlinlang.org/docs/null-safety.html#nullable-types-and-non-nullable-types | ||
| [ref-safe-call]: https://kotlinlang.org/docs/null-safety.html#safe-call-operator | ||
| [ref-not-null]: https://kotlinlang.org/docs/null-safety.html#not-null-assertion-operator | ||
| [ref-let]: https://kotlinlang.org/docs/null-safety.html#let-function | ||
| [ref-elem-at-or-null]: https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.text/element-at-or-null.html | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| # Introduction | ||
|
|
||
| In an ideal world, we could rely on everything being nicely defined, with concrete values and no gaps. | ||
|
|
||
| This conflicts with our everyday experience, and thus many programming languages have a way to represent the absence of a value: null or NULL, nil, None, nothing... | ||
|
|
||
| This was easy to introduce (back in 1965), but soon led to regrets. | ||
| The inventor later called it his "Billion Dollar Mistake". | ||
|
|
||
| In recent decades, language designers have explored many ways to make nulls available but (mostly) safe. | ||
|
|
||
| Kotlin has a few [general principles][ref-null-safety], which differ significantly from Java. | ||
|
|
||
|
|
||
| - Kotlin has a special `null` value. | ||
| - All types _default_ to being non-nullable. | ||
| - Most types can be _made_ nullable, but the programmer must explicitly order this. | ||
| - Functions which might fail with an exception are likely to have a variant with a nullable return value. | ||
| - Special, highly terse syntax tries to make testing for and responding to nulls as easy as possible. | ||
|
|
||
| ## Creating nullable variables | ||
|
|
||
| Standard types generally have a nullable equivalent, with a `?` suffix: for example `String?` instead of `String`. | ||
|
|
||
| ```kotlin | ||
| var a = "Kotlin" // inferred type is String | ||
| a = null // => Error: Null can not be a value of a non-null type String | ||
|
|
||
| var b: String? = "Kotlin" // nullable type | ||
| b = null // => b is now null | ||
| b == null // => true | ||
| ``` | ||
|
|
||
| ## Useful operators and functions | ||
|
|
||
| ### The safe call operator `?.` | ||
|
|
||
| For a non-nullable `String`, we can get the `.length` property. | ||
|
|
||
| A nullable `String?` fails with the same syntax, but using `?.length` allows for the possibility of a `null`. | ||
| The return type is `Int?`. | ||
|
|
||
| ```kotlin | ||
| var a = "Kotlin" | ||
| a.length // => 6 | ||
|
|
||
| // b is still null | ||
| b.length // => Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String? | ||
| b?.length // => null | ||
| a?.length // => 6 | ||
| ``` | ||
|
|
||
| As we saw in the last line above, `?.` also works with non-nullable types. | ||
|
|
||
| ### The Elvis operator `?:` | ||
|
|
||
| Extending the idea of the safe-call operator, the [Elvis operator][wiki-elvis] lets us supply a default value to replace `null`. | ||
|
|
||
| ```kotlin | ||
| // b is still null | ||
| b?.length ?: -1 // => -1, in place of null | ||
| a?.length ?: -1 // => 6, as before | ||
| ``` | ||
|
|
||
| ### Not-null assertion operator `!!` | ||
|
|
||
| With this operator, a nullable type is forced to be treated as non-null, when the programmer is confident about this. | ||
| Perhaps we sometimes understand the program logic better than an over-cautious compiler? | ||
|
|
||
| ```kotlin | ||
| // b is still a String? | ||
| b = "Kotlin?" | ||
| b!!.length // => 7 | ||
| ``` | ||
|
|
||
| The responsibility is now on us: if `b` is still `null` we get a `NullPointerException`. | ||
|
|
||
| ### Let function `?.let` | ||
|
|
||
| If passing a nullable item to a block of code, we could do specific null checks `if (item != null) { do something }`. | ||
|
|
||
| It is simpler and more idiomatic to use `item?.let { do something }`, | ||
| The `do something` will only execute when `item` is not null. | ||
|
|
||
| ### Functions of type `somethingOrNone()` | ||
|
|
||
| Some functions may fail, for example when asking for an invalid index in a string or list. | ||
|
|
||
| Rather than throwing an exception, we could use the `...OrNull` variant of the function, to get (in this case) an `Int?` return value. | ||
|
|
||
| ```kotlin | ||
| val str = "Kotlin" | ||
| str.elementAtOrNull(10) // => null | ||
| str.elementAt(10) // => StringIndexOutOfBoundsException: String index out of range: 10 | ||
| ``` | ||
|
|
||
|
|
||
| [web-mistake]: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/ | ||
| [ref-null-safety]: https://kotlinlang.org/docs/null-safety.html | ||
| [wiki-elvis]: https://en.wikipedia.org/wiki/Elvis_operator |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| [ | ||
| { | ||
| "url": "https://kotlinlang.org/docs/null-safety.html", | ||
| "description": "Kotlin introduction to null safety." | ||
| }, | ||
| { | ||
| "url": "https://kotlinlang.org/docs/java-to-kotlin-nullability-guide.html", | ||
| "description": "Nullability guide for Java to Kotlin migration." | ||
| } | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.