Control Flow
Keel provides several ways to control program flow. Unlike imperative languages, all control flow constructs in Keel are expressions that return values.
If Expressions
The if-then-else expression evaluates to one of two values:
let x = 5
let result = if x > 0 then "positive" else "non-positive"
result
Try itBoth branches must have the same type:
let condition = True
-- Valid: both branches return Int
if condition then 1 else 0
Try itInvalid example (branches have different types):
if condition then 1 else "zero" -- Error: type mismatch
Multi-line If
let condition = True
if condition then
"yes"
else
"no"
Try itMulti-way Conditionals
Chain conditions with else if:
let score = 85
let grade =
if score >= 90 then "A"
else if score >= 80 then "B"
else if score >= 70 then "C"
else if score >= 60 then "D"
else "F"
grade -- "B"
Try itCase Expressions
Pattern match on values with case:
type Color = Red | Green | Blue
let color = Green
let description = case color of
Red -> "The color of fire"
Green -> "The color of nature"
Blue -> "The color of sky"
description
Qualified Enum Patterns
Patterns support the same qualified :: syntax used in expressions:
type Color = Red | Green | Blue
let color = Color::Green
case color of
Color::Red -> "fire"
Color::Green -> "nature"
Color::Blue -> "sky"
Matching Multiple Patterns
Use | to match multiple patterns (or-patterns):
type Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
let day = Saturday
let isWeekend = case day of
Saturday | Sunday -> True
_ -> False
isWeekend -- True
Matching with Guards
Add conditions to patterns using if:
let number = -5
case number of
n if n < 0 -> "negative"
n if n == 0 -> "zero"
n if n > 0 -> "positive"
_ -> "unknown"
Try itMatching Nested Structures
Pattern match deeply nested data:
let data = (3, 4)
case data of
(a, b) -> a + b
Try itMaybe Handling
Handle optional values with case:
let maybeUser = Just "Alice"
let displayName = case maybeUser of
Just name -> name
Nothing -> "Anonymous"
displayName
Try itThe Maybe type is defined as:
type Maybe a = Just a | Nothing
Result Handling
Handle success/failure with Result:
let parseResult = Ok 42
case parseResult of
Ok n -> "Parsed: " ++ show n
Err msg -> "Error: " ++ msg
The Result type is defined as:
type Result a e = Ok a | Err e
List Pattern Matching
Destructure lists in case expressions:
let numbers = [1, 2, 3]
case numbers of
[] -> "empty list"
[x] -> "single element"
[x, y] -> "two elements"
x :: xs -> "multiple elements"
Try itProcessing Lists Recursively
fn sum : List Int -> Int
fn sum list = case list of
[] -> 0
x :: xs -> x + sum xs
sum [1, 2, 3, 4, 5] -- 15
Try itMultiple Head Elements
let list = [1, 2, 3, 4]
case list of
a :: b :: rest -> a + b
x :: xs -> x
[] -> 0
Try itExhaustiveness Checking
The compiler ensures all cases are handled:
type Color = Red | Green | Blue
let color = Blue
case color of
Red -> "red"
Green -> "green"
Blue -> "blue"
Use _ for catch-all patterns:
type Color = Red | Green | Blue
let color = Green
case color of
Red -> "primary"
_ -> "other"
Guard Exhaustiveness
Add a catch-all pattern to ensure completeness:
let x = 0
case x of
n if n > 0 -> "positive"
n if n < 0 -> "negative"
_ -> "zero"
Try itBest Practices
- Prefer case over if for matching on types
- Handle all cases explicitly when possible
- Use guards for complex conditions
- Keep pattern matches shallow — deep nesting is hard to read
- Put specific patterns first, catch-all patterns last
Next Steps
Learn about collections to work with lists, tuples, and records.