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:
import IO exposing (print)
let userName = "Alice"
let userAge = 30
print usrName
-- Error: Variable 'usrName' not found. Did you mean 'userName' or 'userAge'?
Try itMultiple suggestions when relevant:
import IO exposing (print)
let userName = "Alice"
let userAge = 30
let userEmail = "alice@example.com"
print usrNme
-- Error: Variable 'usrNme' not found. Did you mean 'userName', 'userAge', or 'userEmail'?
Try itSuggestions 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'?
Try itParser Errors
Keel provides clear messages for syntax errors.
Indentation Errors
fn example : Int -> Int
fn example x =
let y = 1 -- Error: Function body must be indented by at least 4 spaces
x + y
Try itBlock Alignment
if True then
0
else -- Error: 'else' must align with 'if'
1
Try itBlock Nesting
let x =
x + 1 -- Error: Block must be indented more than parent
-- Hint: Indent the block to 4 spaces
Try itType Definition Errors
type Direction = North | south -- Error: Enum variant must start with uppercase
Try itRecord 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.
Try itEnum 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.
Try itDelimiter Balancing
Keel checks for mismatched, unclosed, and unexpected delimiters before parsing, producing precise error messages:
let x = (1 + 2
-- Error: Unclosed '(' — expected ')'
Try itlet x = [1, 2)
-- Error: Mismatched delimiter — '(' expected ')' but found ')'
Try itlet x = 1 + 2]
-- Error: Unexpected ']' — no matching '['
Try itTuple 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)|`.
Try itCompiler Errors
Undeclared Symbols
let x = unknownVar -- Error: Variable 'unknownVar' is not declared
Try itType Mismatches
fn add: Int -> Int -> Int
fn add x y =
x + y
add "hello" 5 -- Error: Expected Int but got String
Try itGeneric 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
Try itFix by adding a type annotation:
import Json
let data: Result { x: Int } String = Json.parse "{\"x\": 1}"
data
Try itScope Violations
fn example : Int -> Int
fn example x =
let inner = x + 1
inner
inner -- Error: Variable 'inner' is not in scope
Try itHelpful 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" }`
Try itCorrect 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
Try itCorrect 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
Try itCorrect syntax:
let present = Just 42
let absent = 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
Try itCorrect syntax examples:
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
Try itWrong Constructor Type
case Just 5 of
Ok n -> n -- Error: Pattern type mismatch: expected Maybe, but pattern is Result (Ok)
_ -> 0
Try itGuard Type Errors
let x = 2
case x of
n if n + 1 -> "bad" -- Error: Guard expression must be Bool, found Int
_ -> "ok"
Try itCorrect guard syntax:
let x = 5
case x of
n if n > 0 -> "positive"
n if n < 0 -> "negative"
_ -> "zero"
Try itBranch Type Mismatch
let x = 2
case x of
1 -> 42 -- Int
2 -> "hello" -- Error: Case branches have incompatible types: Int and String
_ -> 0
Try itCorrect (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
Try itimport String
String.length 42
-- Error: Type mismatch: expected String, found Int
-- Hint: Function signature: String -> Int
-- Expected argument type: String, but got: Int
Try itThe 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
Try itUnsafe access (use List.nth for 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
let mapped = Ok 5 |> Result.map (|x| x * 2)
-- Chain operations that might fail
let chained = Ok 5 |> Result.andThen (|x| if x > 0 then Ok x else Err "negative")
-- Provide default values
let defaulted = Err "failed" |> Result.withDefault 0
-- Transform error values
Err "bad" |> Result.mapError (|e| "Error: " ++ e)
Try itMaybe Module
Transform and chain optional values:
import Maybe
-- Transform present values
let mapped = Just 5 |> Maybe.map (|x| x * 2)
-- Chain operations that might return Nothing
let chained = Just 5 |> Maybe.andThen (|x| if x > 0 then Just x else Nothing)
-- Provide default values
Nothing |> Maybe.withDefault 0
Try itConverting Between Types
import Result
-- Maybe to Result (with error message)
let a = Just 5 |> Result.fromMaybe "was nothing"
-- Result to Maybe (discards error)
let c: Maybe Int = Ok 5 |> Result.toMaybe
let d: Result Int String = Err "x"
d |> Result.toMaybe
Try itBest 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.