Esc
Start typing to search...

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 it

Boolean 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 it

Null/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 it

Keywords 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 it

Pattern 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 it

Branch 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 it

Type 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 it

Composable 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

  1. Read error messages carefully — they often contain the exact fix
  2. Check the hints — suggestions point to Keel-specific syntax
  3. Use the playground — experiment with syntax in the browser
  4. Handle all cases — the compiler warns about incomplete pattern matches
  5. Use Result/Maybe modules — prefer map and andThen over nested pattern matching

Next Steps

Explore the Standard Library to see available modules and functions.