Error Handling
Keel provides comprehensive error messages with helpful suggestions to improve developer experience.
Fuzzy Matching ("Did you mean?")
When you reference an undefined symbol, Keel suggests similar names:
let userName = "Alice"
let userAge = 30
print usrName
-- Error: Variable 'usrName' is not declared. Did you mean 'userName'?
Multiple suggestions when relevant:
let userName = "Alice"
let userAge = 30
let userEmail = "alice@example.com"
print usrNme
-- Error: Variable 'usrNme' is not declared. Did you mean 'userName', 'userAge', or 'userEmail'?
Suggestions work for:
- Variables and bindings
- Function names
- Module names
- Enum types and variants
- Type aliases
Module Function Typos
Suggestions also work for module function names:
import List
List.fold [1, 2, 3] 0
-- Error: Function 'fold' is not declared in module 'List'. Did you mean 'foldl' or 'foldr'?
Parser Errors
Keel provides clear messages for syntax errors.
Indentation Errors
fn example x =
let y = 1 -- Error: Expected indentation of at least 4 spaces
x + y
Block Alignment
if condition then
result1
else -- Error: 'else' must align with 'if'
result2
Block Nesting
let x =
x + 1 -- Error: Block must be indented more than parent
-- Hint: Indent the block to 4 spaces
Type Definition Errors
type Direction = North | south -- Error: Enum variant must start with uppercase
Record Type Alias Syntax
Using type instead of type alias for record types:
type Person = { name: String, age: Int }
-- Error: Expected enum variant, found record syntax
-- Hint: Use 'type alias' to define a record type alias.
Enum Variant Parentheses
Forgetting parentheses in enum variant data:
type Result a e = Ok a | Err e
-- Result OkType ErrType: first parameter is success, second is error
-- Error: Enum variant 'Ok' appears to have data without parentheses
-- Hint: Use `Ok(a)` instead of `Ok a`. Variant data requires parentheses.
Delimiter Balancing
Keel checks for mismatched, unclosed, and unexpected delimiters before parsing, producing precise error messages:
let x = (1 + 2
-- Error: Unclosed '(' — expected ')'
let x = [1, 2)
-- Error: Mismatched delimiter — '(' expected ')' but found ')'
let x = 1 + 2]
-- Error: Unexpected ']' — no matching '['
Tuple Pattern Type Annotations
Annotating a tuple pattern as a whole instead of its elements:
let addPair = |(x, y): (Int, Int)| x + y
-- Error: Type annotations on tuple patterns must be on individual elements
-- Hint: Use `|(x: Int, y: Int)|` instead of `|(x, y): (Int, Int)|`.
Compiler Errors
Undeclared Symbols
let x = unknownVar -- Error: Variable 'unknownVar' is not declared
Type Mismatches
fn add : Int -> Int -> Int
fn add x y = x + y
add "hello" 5 -- Error: Expected Int but got String
Generic Type Annotations Required
Functions that return a generic type (like Json.parse) require a type annotation on the let binding so the compiler knows the concrete type:
import Json
let data = Json.parse "{\"x\": 1}"
-- Error: Generic function result requires a type annotation for variable 'data'
-- Hint: Add a type annotation to specify the expected type
Fix by adding a type annotation:
import Json
let data: Result { x: Int } String = Json.parse "{\"x\": 1}"
Scope Violations
let x =
let inner = 5
inner
print inner -- Error: Variable 'inner' is not in scope
Helpful Suggestions for Common Mistakes
Keel detects common syntax patterns from other languages and provides helpful suggestions.
JavaScript-Style Record Syntax
{ name: "Alice", age: 30 }
-- Error: JavaScript-style record syntax is not allowed
-- Hint: Use `=` instead of `:` for record field assignment
-- Example: `{ name = "Alice" }`
Correct syntax:
{ name = "Alice", age = 30 }
Try itBoolean Literals from Other Languages
let x = true -- Error: 'true' is not a Keel keyword
-- Hint: Use `True` for boolean true
Correct syntax:
let x = True
let y = False
x && y
Try itNull/Nil/None from Other Languages
let x = null -- Error: 'null' is not a Keel keyword
-- Hint: Use `Nothing` for absent values
Correct syntax:
let present: Maybe Int = Just 42
let absent: Maybe Int = Nothing
case present of
Just n -> "Got a value"
Nothing -> "Nothing"
Try itKeywords from Other Languages
return 5 -- Hint: Keel is expression-based; the last expression is the return value
match x of -- Hint: Use `case ... of` for pattern matching
function add -- Hint: Use `fn` to define functions
var x = 5 -- Hint: Use `let` for variable bindings
Correct syntax examples:
-- Functions return the last expression (no return keyword)
fn double : Int -> Int
fn double x = x * 2
-- Pattern matching uses case...of
let value = Just 5
case value of
Just n -> n * 2
Nothing -> 0
Try itPattern Matching Errors
Type Mismatch in Patterns
case 42 of
"hello" -> 1 -- Error: Pattern type mismatch: expected Int, but pattern is String
_ -> 0
Wrong Constructor Type
case Just 5 of
Ok n -> n -- Error: Pattern type mismatch: expected Maybe, but pattern is Result (Ok)
_ -> 0
Guard Type Errors
case x of
n if n + 1 -> "bad" -- Error: Guard expression must be Bool, found Int
_ -> "ok"
Correct guard syntax:
let x = 5
case x of
n if n > 0 -> "positive"
n if n < 0 -> "negative"
_ -> "zero"
Try itBranch Type Mismatch
case x of
1 -> 42 -- Int
2 -> "hello" -- Error: Case branches have incompatible types: Int and String
_ -> 0
Correct (all branches return same type):
let x = 2
case x of
1 -> "one"
2 -> "two"
_ -> "other"
Try itType Errors
Function Argument Type Mismatch
When you pass the wrong type to a function, Keel shows the expected and actual types along with the full function signature:
import List
List.map 42 [1, 2, 3]
-- Error: Type mismatch: expected (a -> b), found Int
-- Hint: Function signature: (a -> b) -> [a] -> [b]
-- Expected argument type: (a -> b), but got: Int
import String
String.length 42
-- Error: Type mismatch: expected String, found Int
-- Hint: Function signature: String -> Int
-- Expected argument type: String, but got: Int
The function signature in the hint helps you understand what the function expects, especially for higher-order functions with type variables.
Runtime Errors
Collection Bounds
let items = [1, 2, 3]
items[10] -- Error: Index 10 out of bounds for list of size 3
Safe access:
let items = [1, 2, 3]
items[0] -- 1
Try itComposable Error Handling
Keel provides the Result and Maybe modules for functional, composable error handling without nested pattern matching.
Result Module
Transform and chain operations that might fail:
import Result
-- Transform success values
Ok 5 |> Result.map (|x| x * 2) -- Ok 10
-- Chain operations that might fail
Ok 5 |> Result.andThen (|x| if x > 0 then Ok x else Err "negative")
-- Provide default values
Err "failed" |> Result.withDefault 0 -- 0
-- Transform error values
Err "bad" |> Result.mapError (|e| "Error: " ++ e)
Maybe Module
Transform and chain optional values:
import Maybe
-- Transform present values
Just 5 |> Maybe.map (|x| x * 2) -- Just 10
-- Chain operations that might return Nothing
Just 5 |> Maybe.andThen (|x| if x > 0 then Just x else Nothing)
-- Provide default values
Nothing |> Maybe.withDefault 0 -- 0
Converting Between Types
import Result
import Maybe
-- Maybe to Result (with error message)
Just 5 |> Result.fromMaybe "was nothing" -- Ok 5
Nothing |> Result.fromMaybe "was nothing" -- Err "was nothing"
-- Result to Maybe (discards error)
Ok 5 |> Result.toMaybe -- Just 5
Err "x" |> Result.toMaybe -- Nothing
Best Practices
- Read error messages carefully — they often contain the exact fix
- Check the hints — suggestions point to Keel-specific syntax
- Use the playground — experiment with syntax in the browser
- Handle all cases — the compiler warns about incomplete pattern matches
- Use Result/Maybe modules — prefer
mapandandThenover nested pattern matching
Next Steps
Explore the Standard Library to see available modules and functions.