Skip to content
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

Kotlin / Bob / DigDeeper: Add a pattern matching approach using when-expression #629

Merged
merged 6 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions exercises/practice/bob/.approaches/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@
"authors": [
"bobahop"
]
},
{
"uuid": "7aebc1df-c23a-42c9-90c4-f022e40d67e9",
"slug": "when-expression",
"title":"when expression",
"blurb": "Use a when expression to return the answer.",
"authors": [
"micha-b"
]
}
]
}
18 changes: 18 additions & 0 deletions exercises/practice/bob/.approaches/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ object Bob {

For more information, check the [Answer `List` approach][approach-answer-list].

## Approach: `when` expression

```kotlin
object Bob {
fun hey(statement: String): String =
when {
statement.isQuestion() && statement.isYelling() -> "Calm down, I know what I'm doing!"
statement.isQuestion() -> "Sure."
statement.isYelling() -> "Whoa, chill out!"
statement.isSilence() -> "Fine. Be that way!"
else -> "Whatever."
}
}
```

For more information, check the [`when` expression approach][approach-when]

## Which approach to use?

The choice between `if` expressions and answers `List` can be made by perceived readability.
Expand All @@ -65,3 +82,4 @@ The choice between `if` expressions and answers `List` can be made by perceived
[dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
[approach-if]: https://exercism.org/tracks/kotlin/exercises/bob/approaches/if-expressions
[approach-answer-list]: https://exercism.org/tracks/kotlin/exercises/bob/approaches/answer-list
[approach-when]: https://exercism.org/tracks/kotlin/exercises/bob/approaches/when-expression
54 changes: 54 additions & 0 deletions exercises/practice/bob/.approaches/when-expression/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# `when` expressions

```kotlin
object Bob {
fun hey(statement: String): String =
when {
statement.isQuestion() && statement.isYelling() -> "Calm down, I know what I'm doing!"
statement.isQuestion() -> "Sure."
statement.isYelling() -> "Whoa, chill out!"
statement.isSilence() -> "Fine. Be that way!"
else -> "Whatever."
}

private fun String.isSilence(): Boolean = this.isBlank()
private fun String.isQuestion(): Boolean = this.trim().endsWith('?')
Comment on lines +14 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return types could be omitted (feel free to ignore this suggestion)

Suggested change
private fun String.isSilence(): Boolean = this.isBlank()
private fun String.isQuestion(): Boolean = this.trim().endsWith('?')
private fun String.isSilence() = this.isBlank()
private fun String.isQuestion() = this.trim().endsWith('?')

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually really like explicit return types because I think it makes the intent and outcome of a function easier to read.

But I know that you are able to omit them in Kotlin if you want to.
But I usually don't omit them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that's perfectly fine :)

private fun String.isYelling(): Boolean = any(Char::isLetter) && toUpperCase() == this
}
```

In this approach you have a `when` expression containing different so-called branches that can be matched using the corresponding patterns.
As soon as one of these patterns on the left side of the arrow (`->`) is matched ...

1. the value on the right side
2. the block on the right side is executed and the value retuned from the block

... is returned from the `when` expression.

Only one branch is matched in one execution of the `when` expression. The branches are matched from top to bottom and the first branch in which the condition evaluates to `true` is selected.
If none of the given conditions matches the `else` branch is selected and returned.

~~~~exercism/caution
Depending on what patterns are on the left side of your branches the order of branches is important to the correct execution of the `when` expression since the first matching branch is selected.
~~~~

An [object declaration][object] is used to define `Bob` as essentially a [singleton][singleton] object instantiation of the class.
This is sufficient, since there is no object state that needs to change with each call of the `hey` method.

Inside this object there are some `private` [extension methods as members][extension-members] of the `object`. This adds these methods to the `String` data type for `private` usage of these methods in this `object`. This allows calling the methods directly on the `String` instead of passing the `String` to the method.
(More about extension methods in general [here][extension-general])

These extension methods check for:

1. `isSilence()`: a blank string
2. `isQuestion()`: a string with the last non-whitespace-character being a question mark
3. `isYelling()`: a string with all letters in uppercase

When combining these methods as in the `when` expression above you can map all the cases required by the exercise.


[when]: https://kotlinlang.org/docs/control-flow.html#when-expression
[object]: https://kotlinlang.org/docs/object-declarations.html#object-declarations-overview
[singleton]: https://en.wikipedia.org/wiki/Singleton_pattern
[extension-members]: https://kotlinlang.org/docs/extensions.html#declaring-extensions-as-members
[extension-general]: https://kotlinlang.org/docs/extensions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fun hey(statement: String): String =
when {
statement.isQuestion() && statement.isYelling() -> "Calm down, I know what I'm doing!"
statement.isQuestion() -> "Sure."
statement.isYelling() -> "Whoa, chill out!"
statement.isSilence() -> "Fine. Be that way!"
else -> "Whatever."
}