Optional
Optional[T] represents a value that might not be there. It's an enum (this is the actual stdlib declaration — you don't write it yourself):
enum Optional[T] {
case Some(T)
case None
}
There are no null references in Kestrel — the null literal is just sugar for Optional.None. Anything that might be absent — a dictionary lookup, a parse, a "first matching" search — returns an Optional[T], and the compiler forces the caller to handle both cases.
Constructing
Most of the time, the stdlib hands you an Optional and you don't construct one yourself. When you do, write the case explicitly or rely on optional promotion:
let nothing: Optional[Int] = .None;
let something: Optional[Int] = .Some(42);
func cached(key: String) -> Optional[String] {
"hit" // promoted to .Some("hit") at return
}
Unwrapping with if let
The most common pattern:
func lookup(id: Int) -> Optional[String] {
if id == 7 { .Some("Ada") } else { .None }
}
func greet(user: String) {
println("hello, \(user)");
}
if let .Some(user) = lookup(7) {
greet(user);
} else {
println("not found");
}
user is in scope inside the if block, already unwrapped.
Unwrapping with match
When you want to handle both cases as expressions:
func setting(name: String) -> Optional[String] {
if name == "theme" { .Some("dark") } else { .None }
}
let label = match setting("theme") {
.Some(value) => value,
.None => "default"
};
Mapping and chaining
Optional has helper methods for common cases:
let name: Optional[String] = .Some("Ada");
let length = name.map { it.chars.count }.unwrap(or: 0);
map applies a function inside the Some; unwrap(or:) provides a fallback for None. Use these when you'd otherwise be writing a one-liner match. (Keep the chain on one line — a continuation line starting with . doesn't parse.)
When to use Optional vs Result
Optional says something or nothing. Result says something or a reason. If the caller needs to know why something is missing, return Result — Optional[T] throws away that information.