7.6 KiB
The when statement (expression)
Lyng provides a concise multi-branch selection with when, heavily inspired by Kotlin. In Lyng, when is an expression: it evaluates to a value. If the selected branch contains no value (e.g., it ends with void or calls a void function like println), the whole when expression evaluates to void.
Currently, Lyng implements the "subject" form: when(value) { ... }. The subject-less form when { condition -> ... } is not implemented yet.
Quick examples
val r1 = when("a") {
"a" -> "ok"
else -> "nope"
}
assertEquals("ok", r1)
val r2 = when(5) {
3 -> "no"
4 -> "no"
else -> "five"
}
assertEquals("five", r2)
val r3 = when(5) {
3 -> "no"
4 -> "no"
}
// no matching case and no else → `void`
assert(r3 == void)
>>> void
Syntax
when(subject) {
condition1 [, condition2, ...] -> resultExpressionOrBlock
conditionN -> result
else -> fallback
}
- Commas group multiple conditions for one branch.
- First matching branch wins; there is no fall‑through.
elseis optional. If omitted and nothing matches, the result isvoid.
Matching rules (conditions)
Within when(subject), each condition is evaluated against the already evaluated subject. Lyng supports:
- Equality match (default)
-
Any expression value can be used as a condition. It matches if it is equal to
subject. -
Equality relies on Lyng’s comparison (
compareTo(...) == 0). For user types, implement comparison accordingly.when(x) { 0 -> "zero" "EUR" -> "currency" }
void
- Type checks:
isand!is
-
Check whether the subject is an instance of a class.
-
Works with built‑in classes and user classes.
fun typeOf(x) { when(x) { is Real, is Int -> "number" is String -> "string" else -> "other" } } assertEquals("number", typeOf(5)) assertEquals("string", typeOf("hi"))
void
- Containment checks:
inand!in
in containermatches ifcontainer.contains(subject)is true.!in containermatches ifcontains(subject)is false.- Any object that provides
contains(item)can act as a container.
Common containers:
- Ranges (e.g.,
'a'..'z',1..10,1..<10,..5,5..) - Lists, Sets, Arrays, Buffers
- Strings (character or substring containment)
Examples:
when('e') {
in 'a'..'c' -> "small"
in 'a'..'z' -> "letter"
else -> "other"
}
>>> "letter"
when(5) {
in [1,2,3,4,6] -> "no"
in [7,0,9] -> "no"
else -> "ok"
}
>>> "ok"
when(5) {
in [1,2,3,4,6] -> "no"
in [7,0,9] -> "no"
in [-1,5,11] -> "yes"
else -> "no"
}
>>> "yes"
when(5) {
!in [1,2,3,4,6,5] -> "no"
!in [7,0,9,5] -> "no"
!in [-1,15,11] -> "ok"
else -> "no"
}
>>> "ok"
// String containment
"foo" in "foobar" // true (substring)
'o' in "foobar" // true (character)
>>> true
Notes on mixed String/Char ranges:
-
Prefer character ranges for characters:
'a'..'z'. -
"a".."z"is a String range and may not behave as you expect withCharsubjects.assert( "more" in "a".."z") assert( 'x' !in "a".."z") // Char vs String range: often not what you want assert( 'x' in 'a'..'z') // OK assert( "x" !in 'a'..'z') // String in Char range: likely not intended
void
Grouping multiple conditions with commas
You can group values and/or is/in checks for a single result:
fun classify(x) {
when(x) {
"42", 42 -> "answer"
is Real, is Int -> "number"
in ['@', '#', '^'] -> "punct1"
in "*&.," -> "punct2"
else -> "unknown"
}
}
assertEquals("number", classify(π/2))
assertEquals("answer", classify(42))
assertEquals("answer", classify("42"))
>>> void
Return value and blocks
-
whenreturns the value of the matched branch result expression/block. -
Branch bodies can be single expressions or blocks
{ ... }. -
If a matched branch produces
void(e.g., only prints), thewhenresult isvoid.val res = when(2) { 1 -> 10 2 -> { println("two"); 20 } else -> 0 } assertEquals(20, res)
void
Else branch
- Optional but recommended when non‑exhaustive.
- If omitted and nothing matches,
whenresult isvoid(see r3 in the Quick examples). - Only one
elseis allowed.
Subject‑less when
The Kotlin‑style subject‑less form when { condition -> ... } is not implemented yet in Lyng. Use if/else chains or structure your checks around a subject with when(subject) { ... }.
Extending when for your own types
Equality matches
- Equality checks in
when(subject)use Lyng comparison (compareTosemantics under the hood). For your own Lyng classes, implement comparison appropriately so thatsubject == valueworks as intended.
in / !in containment
-
Provide a
contains(item)method on your class to participate ininconditions. -
Example: a custom
Boxthat contains one specific item:class Box(val item) fun Box.contains(x) { x == item }
val b = Box(10) when(10) { in b -> "hit" }
"hit"
Any built‑in collection (List, Set, Array), Range, Buffer, and other containers already implement contains.
Type checks (is / !is)
-
Every value has a
::classthat yields its Lyng class object, e.g.[1,2,3]::class→List. -
is ClassNameinwhenuses Lyng’s class hierarchy. Ensure your class is declared and can be referenced by name.[]::class == List
true
fun f(x) { when(x) { is List -> "list" else -> "other" } } assertEquals("list", f([1]))
void
Kotlin‑backed classes (embedding)
When embedding Lyng in Kotlin, you may expose Kotlin‑backed objects and classes. Interactions inside when work as follows:
ischecks use the Lyng class object you expose for your Kotlin type. Ensure your exposed class participates in the Lyng class hierarchy (see Embedding docs).inchecks callcontains(subject); if your Kotlin‑backed object wants to supportin, expose acontains(item)method (mapped to Lyng) or implement the corresponding Lyng container wrapper.- Equality follows Lyng comparison rules. Ensure your Kotlin‑backed object’s Lyng adapter implements equality/compare correctly.
For details on exposing classes/methods from Kotlin, see: Embedding Lyng in your Kotlin project.
Gotchas and tips
- First match wins; there is no fall‑through. Order branches carefully.
- Group related conditions with commas for readability and performance (a single branch evaluation).
- Prefer character ranges for character tests; avoid mixing
StringandCharranges. - If you rely on
in, check that your container implementscontains(item). - Remember:
whenis an expression — you can assign its result to a variable or return it from a function.
Additional examples
fun label(ch) {
when(ch) {
in '0'..'9' -> "digit"
in 'a'..'z', in 'A'..'Z' -> "letter"
'$' -> "dollar"
else -> "other"
}
}
assertEquals("digit", label('3'))
assertEquals("dollar", label('$'))
>>> void
fun normalize(x) {
when(x) {
is Int -> x
is Real -> x.round()
else -> 0
}
}
assertEquals(12, normalize(12))
assertEquals(3, normalize(2.6))
>>> void