Kotlin 73
Two small changes for your amusement. UPDATE: No, three! Thanks Christian!
moveItemTo
In Items, we had:
fun moveItemTo(name: String, target: Items): Boolean {
val maybeItem: Item? = remove(name)
return when (maybeItem) {
null -> false
else -> {
target.add(maybeItem)
return true
}
}
}
We can instead write:
fun moveItemTo(name: String, target: Items): Boolean {
val item = remove(name) ?: return false
target.add(item)
return true
}
That seems nicer to me.
- Details:
- The “elvis” operator,
?:
executes whatever is to its right, only if the expression it follows is null. Here, ifremove
returns an item, it is stored in our localitem
. If it returnsnull
, we immediately exit, returningfalse
. Otherwise, we carry on, completing the move and returningtrue
. -
I think that while this is likely a bit cryptic to Kotlin newbies, it can and should become a valuable tool in our kit when we get used to it: it lets us get rid of nulls quickly and safely. The code above “knows” that
item
is really anItem
, not anItem?
as returned fromremove
.
moveItemTo even more so
Here’s a one-liner, h/t Christian Hujer:
fun moveItemTo(name: String, target: Items): Boolean
= remove(name)?.also {target.add(it)} != null
- Details:
- The
?.
calls the function to its right only if theremove
returns non-null. -
The
also
function passes the result of theremove
, an Item, to its block, asit
. -
That same object, the
remove
result, the Item, is always returned from the block. -
Finally, the result of the
remove
is checked for non-null, returning the correct Boolean.
A very nice concise expression, thanks to Christian Hujer. Might take some getting used to, but when one gets used to these they’ll probably become second nature.
take
A day or so ago, we had this code:
val take = { imperative: Imperative, world: World ->
when {
contents.moveItemTo(imperative.noun, world.inventory) -> {
world.response.say("${imperative.noun} taken.")
}
else -> {
world.response.say("I see no ${imperative.noun} here!")
}
}
}
Today, we have this:
fun take(imp: Imperative, world: World) {
with (world) { imp.noun.let {
response.say(
when (contents.moveItemTo(it, inventory)) {
true -> "$it taken."
false -> "I see no $it here!" }) }}
}
This one I’m not so certain about but I think I prefer it for compactness. I can imagine that it might be harder to grok in fullness, but I’m not certain.
- Details:
- The
with
allows us to skip typingworld
ahead of anything that would require it. Thelet
lets us substituteit
forimp.noun
. -
It’s possible that the use of both
with
andlet
is asking us to put a method somewhere else. I’m not sure, I just got here myself.
Anyway just a couple of nice(?) Kotlin possibilities.
Your thoughts, should you have them, are welcome. See you soon!