Esc
Start typing to search...

Code Examples

Explore Keel through practical code examples. Click any example to open it in the playground.

369 examples

Basics

Collections

Bracket List

collections
let xs: [Int] = [1, 2, 3]
let ys: List Int = [4, 5, 6]
ys

Building Lists

collections
-- Prepend element
let list1 =
    1 :: [2, 3]
    -- Concatenate lists

let list2 =
...

Dataframe Fromrecords

collections
import DataFrame

DataFrame.fromRecords
    [ { name = "Alice", score = 95 }
    , { name = "Bob", score = 87 }
    , { name = "Charlie", score = 92 }
...

Filter

collections
-- Filter: keep matching elements
import List

[ 1, 2, 3, 4]
    |> List.filter (|x| x > 2)
...

Fold

collections
-- Fold: reduce to single value
import List

[1, 2, 3, 4]
    |> List.foldl (|acc x| acc + x) 0
...

Length

collections
import List

fn length: [a] -> Int
fn length list =
    case list of
        [] -> 0
...

List Access

collections
let items =
    [10, 20, 30]

items[0]

List Ops

collections
-- Prepend (cons)
let consed =
    1 :: [2, 3]
    -- Concatenation

let joined =
...

List Pattern

collections
let list =
    [1, 2, 3]

case list of
    [] -> "empty"
    [x] -> "single element"
...

Lists

collections
let numbers =
    [ 1
    , 2
    , 3
    , 4
    , 5
...

Map

collections
-- Map: transform each element
import List exposing (map)

[1, 2, 3]
    |> map (|x| x * 2)  -- [2, 4, 6]

Multiline List

collections
let users =
    [ { name = "Alice", age = 30 }
    , { name = "Bob", age = 25 }
    , { name = "Charlie", age = 35 }
    ]
users

Multiline Record

collections
let config =
    { host = "localhost"
    , port = 8080
    , debug = True
    , maxConnections = 100
    }
...

Nested Record

collections
let data =
    { user = { profile = { name = "Alice" } } }

data.user.profile.name

Record Access

collections
let user =
    { name = "Alice", age = 30 }

user.name

Record Destructure

collections
let person =
    { name = "David", age = 40 }

let { name, age } = person

name

Record Multi Update

collections
let person =
    { name = "Alice", age = 30 }

let updated = { person | age = 31, name = "Alicia" }

updated.name

Record Pattern

collections
let person =
    { name = "Bob", age = 25 }

case person of
    { name, age } -> name

Record Rest

collections
let user =
    { name = "Carol", age = 35, email = "carol@example.com" }

case user of
    { name, .. } -> "Hello, " ++ name

Record Update

collections
-- Record update syntax

type alias Person = { name: String, age: Int }

let person =
    { name = "Alice", age = 30 }
...

Record Workflow

collections
-- Record update creates a new record
let user = { name = "Eve", age = 28 }

let updatedUser = { user | age = user.age + 1 }

updatedUser.name

Records

collections
let user =
    { name = "Alice", age = 30, email = "alice@example.com" }

user.name

Reverse

collections
import List

fn reverse: [a] -> [a]
fn reverse list =
    case list of
        [] -> []
...

Tuple Access

collections
let pair = (1, "hello")

let first = pair.0  -- 1

let second = pair.1  -- "hello"
...

Tuple Access 2

collections
let triple = (True, 42, "world")

triple.2

Tuple Destructure

collections
let (x, y) = (10, 20)

x + y

Tuple Pattern

collections
let pair = (3, 0)

case pair of
    (0, 0) -> "origin"
    (x, 0) -> "on x-axis"
    (0, y) -> "on y-axis"
...

Tuples

collections
let pair = (1, "one")

let triple = (True, 42, "hello")

let point = (3.5, 4.2)
...

Control Flow

Case Enum

control-flow
type Color = Red | Green | Blue

let color = Color::Green

let description =
    case color of
...

Catchall

control-flow
type Color = Red | Green | Blue

let color = Color::Green

case color of
    Color::Red -> "primary"
...

Else If

control-flow
let score = 85

let grade =
    if score >= 90 then "A"
    else if score >= 80 then "B"
    else if score >= 70 then "C"
...

Exhaustive

control-flow
type Color = Red | Green | Blue

let color = Color::Blue

case color of
    Color::Red   -> "red"
...

Guard Exhaustive

control-flow
let x = 0

case x of
    n if n > 0 -> "positive"
    n if n < 0 -> "negative"
    _ -> "zero"

Guards

control-flow
let number = -5

case number of
    n if n < 0 -> "negative"
    n if n == 0 -> "zero"
    n if n > 0 -> "positive"
...

If Expression

control-flow
let x = 5

let result =
    if x > 0 then
        "positive"
    else
...

If Multiline

control-flow
let condition = True

if condition then
    "yes"
else
    "no"

If Same Type

control-flow
let condition = True

-- Valid: both branches return Int
if condition then
    1
else
...

If Type Mismatch

control-flow
if False then 1 else "zero"  -- Error: type mismatch

List Pattern

control-flow
let numbers =
    [1, 2, 3]

case numbers of
    [] -> "empty list"
    [x] -> "single element"
...

Maybe Handling

control-flow
let maybeUser = Just "Alice"

let displayName =
    case maybeUser of
        Just name -> name
        Nothing -> "Anonymous"
...

Maybe Type

control-flow
-- norun
type Maybe a = Just(a) | Nothing

Multiple Head

control-flow
let list = [1, 2, 3, 4]

case list of
    a :: b :: rest -> a + b
    x :: xs -> x
    [] -> 0

Nested Match

control-flow
let data = (3, 4)

case data of
    (a, b) -> a + b

Or Patterns

control-flow
type Day
    = Monday
    | Tuesday
    | Wednesday
    | Thursday
    | Friday
...

Qualified Enum

control-flow
type Color = Red | Green | Blue

let color = Color::Green

case color of
    Color::Red   -> "fire"
...

Recursive Sum

control-flow
fn sum: [Int] -> Int
fn sum list =
    case list of
        [] -> 0
        x :: xs -> x + sum xs
...

Result Handling

control-flow
import String

let parseResult = Ok 42

case parseResult of
    Ok n    -> "Parsed: " ++ String.fromInt n
...

Result Type

control-flow
-- norun
type Result a e = Ok(a) | Err(e)

Dataframe

Expr Aggregation

dataframeexpraggregation
-- Aggregation with DataFrame.Expr
import DataFrame
import DataFrame.Expr as Expr

DataFrame.fromRecords
    [ { group = "A", value = 10 }
...

Expr Arithmetic

dataframeexprarithmetic
-- Arithmetic expressions: column * column
import DataFrame
import DataFrame.Expr as Expr

DataFrame.fromRecords
    [ { price = 10, quantity = 3 }
...

Expr Basics

dataframeexprexpressions
-- DataFrame.Expr for composable column operations
import DataFrame
import DataFrame.Expr as Expr

DataFrame.fromRecords
    [ { name = "Alice", revenue = 100 }
...

Expr Comparison

dataframeexprcomparison
-- Comparison expressions: column >= literal
import DataFrame
import DataFrame.Expr as Expr

DataFrame.fromRecords
    [ { name = "Alice", age = 25 }
...

Expr Conditional

dataframeexprconditional
-- Conditional expressions with DataFrame.Expr
import DataFrame
import DataFrame.Expr exposing (col, lit)
import DataFrame.Expr as Expr

DataFrame.fromRecords
...

Expr Math

dataframeexprmath
-- Math functions: abs
import DataFrame
import DataFrame.Expr as Expr

DataFrame.fromRecords
    [ { value = -3.7 }
...

Expr Null

dataframeexprnull
-- Null handling: fillNull
import DataFrame
import DataFrame.Expr as Expr

DataFrame.fromRecords
    [ { score = 85 }
...

Expr String Ops

dataframeexprstring
-- String operations with DataFrame.Expr
import DataFrame
import DataFrame.Expr as Expr

DataFrame.fromRecords
    [ { name = "alice" }
...

Expr Window

dataframe
import DataFrame
import DataFrame.Expr as Expr

let df = DataFrame.fromRecords
    [ { region = "East", revenue = 100 }
    , { region = "East", revenue = 200 }
...

Lineage Agg Origin

dataframelineageaggregation
-- norun
-- Aggregated columns track source and function
import DataFrame

let df =
    DataFrame.fromRecords
...

Lineage Auto

dataframelineageprovenance
-- norun
-- Lineage appears automatically when printing a DataFrame
import DataFrame

let sales =
    DataFrame.fromRecords
...

Lineage Column

dataframelineage
-- norun
-- DataFrame.columnLineage returns origin info for a column
import DataFrame

let df =
    DataFrame.fromRecords
...

Lineage Dag Join

dataframelineagedag
-- Join produces two parents in the DAG
import DataFrame
import List

let users =
    DataFrame.fromRecords
...

Lineage Full Record

dataframelineage
-- norun
-- Full lineage record structure
import DataFrame

let df =
    DataFrame.fromRecords
...

Lineage Global Ops

dataframelineage
-- norun
-- Global operations (filter, sort) are tracked separately
import DataFrame

let df =
    DataFrame.fromRecords
...

Lineage Join Origin

dataframelineagejoin
-- norun
-- Joined columns track their source DataFrame
import DataFrame

let users =
    DataFrame.fromRecords
...

Lineage Parents Derived

dataframelineagedag
-- Derived DataFrames track parent operations
import DataFrame
import List

let df =
    DataFrame.fromRecords
...

Lineage Parents Root

dataframelineagedag
-- Root DataFrames have no parents
import DataFrame

let df =
    DataFrame.fromRecords
        [ { name = "Alice", age = 30 }
...

Lineage Rename Origin

dataframelineage
-- norun
-- After rename, the transformation tracks the operation
import DataFrame

let df =
    DataFrame.fromRecords
...

Lineage Sourcepath

dataframelineage
-- DataFrame.sourcePath returns Nothing for fromRecords
import DataFrame

let df =
    DataFrame.fromRecords
        [ { name = "Alice", age = 30 }
...

Lineage Tracking

dataframelineageprovenance
-- Track data transformation history
import DataFrame

-- Create data and track transformations
DataFrame.fromRecords [{ product = "Laptop", revenue = 1200 }, { product = "Phone", revenue = 800 }]
    |> DataFrame.filterGt "revenue" 700
...

Lineage Transformations

dataframelineage
-- norun
-- Columns track their transformation history
import DataFrame

let df =
    DataFrame.fromRecords
...

Recode

dataframerecodelabels
-- Remap values with automatic label transfer
import DataFrame

let df =
    DataFrame.fromRecords
        [ { id = 1, score = 1 }
...

Value Labels

dataframelabelsvalue-labels
-- Attach value labels for coded variables
import DataFrame
import ValueLabelSet

let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
...

Variable Labels

dataframelabelsmetadata
-- Variable labels for column descriptions
import DataFrame

let df =
    DataFrame.fromRecords
        [ { id = 1, gender = 1 }
...

Window Functions

dataframeanalyticswindow-functions
-- SQL-style window functions
import DataFrame

-- Sample sales data and window functions
DataFrame.fromRecords [{ product = "Laptop", revenue = 1200 }, { product = "Phone", revenue = 800 }]
    |> DataFrame.withColumn "rank" [1, 2]
...

Date

Datetime

Decimal

Duration

Error Handling

Arg Type Map

error-handling
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

Arg Type String

error-handling
import String

String.length 42
-- Error: Type mismatch: expected String, found Int
-- Hint: Function signature: String -> Int
--       Expected argument type: String, but got: Int

Block Align

error-handling
if True then
    0
  else         -- Error: 'else' must align with 'if'
    1

Block Nest

error-handling
let x =
x + 1          -- Error: Block must be indented more than parent
               -- Hint: Indent the block to 4 spaces

Bool Correct

error-handling
let x = True

let y = False

x && y

Bool Error

error-handling
let x = true     -- Error: 'true' is not a Keel keyword
                 -- Hint: Use `True` for boolean true

Bounds Error

error-handling
let items =
    [1, 2, 3]

items[10]  -- Error: Index 10 out of bounds for list of size 3

Bounds Safe

error-handling
let items =
    [1, 2, 3]

items[0]  -- 1

Branch Correct

error-handling
let x = 2

case x of
    1 -> "one"
    2 -> "two"
    _ -> "other"

Branch Mismatch

error-handling
let x = 2

case x of
    1 -> 42        -- Int
    2 -> "hello"   -- Error: Case branches have incompatible types: Int and String
    _ -> 0

Constructor Type

error-handling
case Just 5 of
    Ok n -> n  -- Error: Pattern type mismatch: expected Maybe, but pattern is Result (Ok)
    _ -> 0

Converting Types

error-handling
import Result

Err "x" |> Result.toMaybe

Converting Types

error-handling
import Result

-- Maybe to Result (with error message)
let a = Just 5 |> Result.fromMaybe "was nothing"

-- Result to Maybe (discards error)
...

Correct Syntax

error-handling
fn double: Int -> Int
fn double x =
    x * 2
    -- Pattern matching uses case...of

let value = Just 5
...

Enum Case

error-handling
type Direction = North | south    -- Error: Enum variant must start with uppercase

Fuzzy Multiple

error-handling
import IO exposing (print)

let userName = "Alice"
let userAge = 30
let userEmail = "alice@example.com"
...

Fuzzy Single

error-handling
import IO exposing (print)
let userName = "Alice"
let userAge = 30
print usrName
-- Error: Variable 'usrName' not found. Did you mean 'userName' or 'userAge'?

Generic Annotation

error-handling
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

Guard Correct

error-handling
let x = 5

case x of
    n if n > 0 -> "positive"
    n if n < 0 -> "negative"
    _ -> "zero"

Guard Type

error-handling
let x = 2

case x of
    n if n + 1 -> "bad"    -- Error: Guard expression must be Bool, found Int
    _ -> "ok"

Indent Error

error-handling
fn example : Int -> Int
fn example x =
let y = 1    -- Error: Function body must be indented by at least 4 spaces
    x + y

Js Record

error-handling
{ name: "Alice", age: 30 }
-- Error: JavaScript-style record syntax is not allowed
-- Hint: Use `=` instead of `:` for record field assignment
-- Example: `{ name = "Alice" }`

Json Fix

error-handling
import Json

let data: Result { x: Int } String = Json.parse "{\"x\": 1}"
data

Keywords

error-handling
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

Maybe Correct

error-handling
let present = Just 42

let absent = Nothing

case present of
    Just n -> "Got a value"
...

Maybe Module

error-handling
import Maybe

Nothing |> Maybe.withDefault 0

Maybe Module

error-handling
import Maybe

-- Transform present values
let mapped = Just 5 |> Maybe.map (|x| x * 2)

-- Chain operations that might return Nothing
...

Maybe Types

error-handlingmayberesult
-- Safe error handling with Maybe
import List
import Maybe

-- Extract values with default fallback
[Just 42, Nothing, Just 7]
...

Mismatched Delim

error-handling
let x = [1, 2)
-- Error: Mismatched delimiter — '(' expected ')' but found ')'

Module Fn Typo

error-handling
import List
List.fold [1, 2, 3] 0
-- Error: Function 'fold' is not declared in module 'List'. Did you mean 'foldl' or 'foldr'?

Null Error

error-handling
let x = null     -- Error: 'null' is not a Keel keyword
                 -- Hint: Use `Nothing` for absent values

Pattern Type

error-handling
case 42 of
    "hello" -> 1  -- Error: Pattern type mismatch: expected Int, but pattern is String
    _ -> 0

Record Correct

error-handling
{ name = "Alice", age = 30 }

Result Module

error-handling
import Result

Err "bad" |> Result.mapError (|e| "Error: " ++ e)

Result Module

error-handling
import Result

-- Transform success values
let mapped = Ok 5 |> Result.map (|x| x * 2)

-- Chain operations that might fail
...

Scope

error-handling
fn example : Int -> Int
fn example x =
    let inner = x + 1
    inner

inner  -- Error: Variable 'inner' is not in scope

Tuple Annotation

error-handling
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)|`.

Type Mismatch

error-handling
fn add: Int -> Int -> Int
fn add x y =
    x + y

add "hello" 5  -- Error: Expected Int but got String

Type Vs Alias

error-handling
type Person = { name: String, age: Int }
-- Error: Expected enum variant, found record syntax
-- Hint: Use 'type alias' to define a record type alias.

Unclosed Paren

error-handling
let x = (1 + 2
-- Error: Unclosed '(' — expected ')'

Undeclared

error-handling
let x = unknownVar    -- Error: Variable 'unknownVar' is not declared

Unexpected Delim

error-handling
let x = 1 + 2]
-- Error: Unexpected ']' — no matching '['

Variant Parens

error-handling
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.

Functions

Arity Overloading 1

functions
module CustomMath exposing (..)

    fn add: Int -> Int
    fn add x =
        x + 1
...

Arity Overloading 2

functions
module CustomMath exposing (..)

    fn add: Int -> Int
    fn add x =
        x + 1
...

Common Filter

functions
import List

-- filter: keep matching elements
[1, 2, 3, 4] |> List.filter (|x| x > 2)

Common Fold

functions
import List

-- fold: reduce to single value
[1, 2, 3, 4] |> List.foldl (|acc x| acc + x) 0

Common Map

functions
import List

-- map: transform each element
[1, 2, 3] |> List.map (|x| x * 2)

Composition

functions
fn double : Int -> Int
fn double x = x * 2

fn addOne : Int -> Int
fn addOne x = x + 1
...

Currying

functions
fn add : Int -> Int -> Int
fn add x y = x + y

let add5 = add 5
add5 3

Defining

functions
fn greet: String -> String
fn greet name =
    "Hello, " ++ name ++ "!"

greet "Alice"  -- "Hello, Alice!"

Duplicate Sig

functions
fn add : Int -> Int
fn add x = x + 1

fn add : Int -> Int
fn add x = x + 2

Factorial

functions
module CustomMath exposing (factorial)

    fn factorial: Int -> Int
    fn factorial n = if n <= 1 then 1 else n * factorial (n - 1)

CustomMath.factorial 5

Factorial Case

functions
fn factorial: Int -> Int
fn factorial n =
    case n of
        0 -> 1
        1 -> 1
        _ -> n * factorial (n - 1)
...

Fibonacci

functions
module CustomMath exposing (fibonacci)

    fn fibonacci: Int -> Int
    fn fibonacci n = if n <= 1 then n else fibonacci (n - 1) + fibonacci (n - 2)

CustomMath.fibonacci 10

Generic Assign

functions
import List

let doubled = List.map (|x| x * 2) [1, 2, 3]

let result = [1, 2, 3, 4, 5]
    |> List.map (|x| x * 2)
...

Generic Compose

functions
fn compose : (b -> c) -> (a -> b) -> a -> c
fn compose g f x = g (f x)

let double = |x: Int| x * 2
let addOne = |x: Int| x + 1
...

Generic Identity

functions
fn identity : a -> a
fn identity x = x

identity 42

Higher Order

functions
-- Takes a function as argument
fn applyTwice: (Int -> Int) -> Int -> Int
fn applyTwice f x =
    f (f x)

applyTwice (|x: Int| x + 1) 5  -- 7

Higher Order Context

functions
module M exposing (..)

    fn apply: (Int -> Int) -> Int -> Int
    fn apply f x =
        f x
...

Lambda As Pattern

functions
let withAlias = |x as m: Int| (x, m)
withAlias 42

Lambda Basics

functions
let addOne = |x: Int| x + 1
let addTwo = |x: Int y: Int| x + y
let addTuple = |(x: Int, y: Int)| x + y

addOne 5

Lambda Error

functions
|x| x + 1

Lambda Map

functions
import List

[1, 2, 3] |> List.map (|x| x * 2)

Lambda Multiline

functions
let compute = |x: Int|
    let y = 3
    x + y

compute 7

Lambda No Annotation

functions
|x| x + 1

Lambda Record Fields

functions
let describe = |{ name: String, age: Int }| name
describe { name = "Alice", age = 30 }

Lambda Rest Pattern

functions
let getName = |{ name: String, .. }| name
getName { name = "Alice", age = 30 }

Lambda Tuple Add

functions
let addPair = |(x: Int, y: Int)| x + y

addPair (3, 4)

Lambda Typed

functions
let f1 = |x: Int| x + 1
let f2 = |x: Int y: Int| x + y
let f3 = |(x: Int, y: Int)| x + y
let f4 = |f: Int -> Int| f 1

f4 (|x: Int| x * 2)

Lambda With Annotation

functions
let f = |x: Int| x + 1
f 1

Local Bindings

functions
import Math

fn quadraticFormula : Float -> Float -> Float -> (Float, Float)
fn quadraticFormula a b c =
    let discriminant = b * b - 4.0 * a * c
    let sqrtDisc = Math.sqrt discriminant
...

Make Adder

functions
fn makeAdder : Int -> (Int -> Int)
fn makeAdder n = |x: Int| x + n

makeAdder 10 5

Native Pipes

functions
import String

"hello" |> String.toUpper |> String.length

Native Pipes

functions
import String
import List
import Math

let a = "hello" |> String.toUpper |> String.length
let b = "hello" |> String.slice 0 3 |> String.toUpper
...

Overloaded Hof

functions
module Apply exposing (..)

    fn apply: (Int -> Int) -> Int -> Int
    fn apply f x =
        f x
...

Overloading Int

functions
module CustomMath exposing (..)

    fn double: Int -> Int
    fn double x =
        x * 2
...

Overloading String

functions
module CustomMath exposing (..)

    fn double: Int -> Int
    fn double x =
        x * 2
...

Overloading Toplevel

functions
fn inc: Int -> Int
fn inc x =
    x + 1

fn inc: Float -> Float
fn inc x =
...

Pattern Matching Fn

functions
fn length : [a] -> Int
fn length list =
    case list of
        [] -> 0
        x :: xs -> 1 + length xs
...

Pipe Chain

functions
fn double: Int -> Int
fn double x =
    x * 2

fn addOne: Int -> Int
fn addOne x =
...

Pipe Operator

functions
-- Pipe operator provides context from the left operand
5
    |> |x| x + 1

Pipeline Example

functions
import List

[1, 2, 3, 4, 5]
    |> List.filter (|x| x > 2)
    |> List.map (|x| x * 2)

Record Field Pipe

functions
-- Mixed typed and untyped fields (untyped inferred from context)
{ x = 1, y = 2 } |> |{ x: Int, y }| x + y

Stdlib Chained Inference

functions
import List

-- Type inference flows through chained operations
-- Each lambda's parameter type is inferred from the previous result
[1, 2, 3, 4, 5]
    |> List.filter (|x| x > 2)
...

Stdlib Filter Inference

functions
import List

-- Lambda parameter type inferred from List.filter signature
List.filter (|x| x > 2) [1, 2, 3, 4, 5]

-- [3, 4, 5]

Stdlib Foldl Inference

functions
import List

-- Accumulator and element types inferred from List.foldl signature
-- foldl : (b -> a -> b) -> b -> List a -> b
-- With initial value 0 (Int) and List Int, acc : Int and x : Int
List.foldl (|acc x| acc + x) 0 [1, 2, 3, 4]
...

Stdlib Map Inference

functions
import List

-- Lambda parameter type inferred from List.map signature
List.map (|x| x * 2)[1, 2, 3]

-- [2, 4, 6]

Stdlib Zipwith Inference

functions
import List

-- Both parameters inferred from List.zipWith signature
-- zipWith : (a -> b -> c) -> List a -> List b -> List c
-- With List Int and List Int, both x : Int and y : Int
List.zipWith (|x y| x * y) [1, 2, 3] [4, 5, 6]
...

Tuple Arg Fst

functions
let pair = (42, "hello")
let (x, _) = pair
x

Tuple Arg Fst

functions
fn fst : (Int, String) -> Int
fn fst pair =
    case pair of
        (x, _) -> x

fst (42, "hello")

Tuple Arg Swap

functions
let pair = (1, "hello")
let (x, y) = pair
(y, x)

Tuple Arg Swap

functions
fn swap : (a, b) -> (b, a)
fn swap pair =
    case pair of
        (x, y) -> (y, x)

swap (1, "hello")

Type Signatures

functions
fn add: Int -> Int -> Int
fn add x y =
    x + y

add 3 4  -- 7

Getting Started

Http

Indentation

Basic Blocks

indentation
-- Function body must be indented
fn addOne : Int -> Int
fn addOne x =
    x + 1

addOne 5  -- 6

Block Nest Correct

indentation
let x =
    1 + 2 + 3
x

Block Nest Fn Call

indentation
let greeting =
    "hello"
greeting

Block Nest Multi Let

indentation
let a =
    5
let b =
    10
a + b

Block Nest Multiline

indentation
let result =
1 + 2 + 3

Block Nest Nested

indentation
let x =
    let y = 3
    let z = 4
    y + z
x

Block Nest String Expr

indentation
let greeting =
"hello"

Block Nest Toplevel

indentation
let x =
x + 1

Case Indentation

indentation
-- Case branches must be indented from 'case'
let value = 2

let result = case value of
    1 -> "one"
    2 -> "two"
...

Case Multiline Body

indentation
-- Case branch bodies can span multiple lines
let value = 2

let result = case value of
    1 ->
        let x = 10
...

Continuation Argument

indentation
-- Multiline arguments work with COLLECTIONS (lists, records, tuples)
-- NOT with simple values like integers

import List

-- This works: list on new indented line continues as argument
...

If Multiline

indentation
-- If-then-else with multiline expressions
let condition = True

let result =
    if condition then
        1 + 2
...

Let Block

indentation
-- Multiple let bindings at same indentation form a block
fn compute : Int -> Int
fn compute x =
    let a = x + 1
    let b = a * 2
    let c = b - 3
...

Let Missing Expr

indentation
let x =

Let Missing Expr Eof

indentation
let a = 10
let b =

List Multiline

indentation
-- Lists can span multiple lines (Elm-style)
import List

let numbers =
    [ 1
    , 2
...

Local Fn Args

indentation
fn add : Int -> Int -> Int
fn add x y = x + y

let result = add 1 2
result

Multiline Function Call

indentation
-- Multi-line function calls work with collections on new lines
import List

-- Lambda on same line, list on indented new line
let doubled = List.map (|x| x * 2)
    [ 1
...

Multiline Module Args

indentation
import List

List.map (|x| x * 2)
    [1, 2, 3]

Pipe Multiline

indentation
-- Pipes work naturally with multiline formatting
import List

let result = [1, 2, 3, 4, 5]
    |> List.map (|x| x * 2)
    |> List.filter (|x| x > 4)
...

Record Multiline

indentation
-- Records can span multiple lines (Elm-style)
let person =
    { name = "Alice"
    , age = 30
    , city = "Paris"
    }
...

Scope Nested

indentation
-- Indentation creates nested scopes within a block
fn example : Int -> Int
fn example x =
    let a = x + 1
    let b = a * 2      -- 'a' is visible here
    let c = a + b      -- both 'a' and 'b' are visible
...

Scope Rules

indentation
fn example : Int -> Int
fn example n =
    let a = n + 1
    let b = a * 2
    let c = a + b
    c
...

Separate Expressions

indentation
-- Lines at the SAME indentation are separate expressions
let x = 1
let y = 2
let z = 3

-- Each let is a separate statement, evaluated in sequence
...

Landing

Lexical

Modules

Access Add

modules
module CustomMath exposing (add, multiply)

    fn add: Int -> Int -> Int
    fn add x y =
        x + y
...

Access Multiply

modules
module CustomMath exposing (multiply)

    fn multiply: Int -> Int -> Int
    fn multiply x y =
        x * y
...

Exposing

modules
-- norun
-- Expose specific items
module exposing (functionA, functionB, TypeA)

-- Expose all
module exposing (..)
...

Fibonacci

modules
module CustomMath exposing (fibonacci)

    fn fibonacci: Int -> Int
    fn fibonacci n =
        if n <= 1 then n
        else fibonacci (n - 1) + fibonacci (n - 2)
...

File Level

modules
module exposing (User, createUser, getName)

type alias User = { name: String, age: Int }

fn createUser : String -> Int -> User
fn createUser name age = { name = name, age = age }
...

Geometry Area

modules
import Math

module Geometry exposing (Shape, area, perimeter)

    type Shape = Circle(Float) | Rectangle(Float, Float) | Triangle(Float, Float, Float)
...

Geometry Perimeter

modules
module Geometry exposing (perimeter)

    fn perimeter: Float -> Float
    fn perimeter r =
        2.0 * 3.14159 * r
...

Import

modules
import String

Import Alias

modules
import Math as Something

Import Alias Exposing

modules
import List as L exposing (length)
length [1, 2]

Import Aliases

modulesimportsaliases
-- Modules and import aliases
import Math as M

import List as L

M.abs (L.length [1, 2, 3])

Import Exposing

modules
import List exposing (map)

-- Using built-in list functions directly
[1, 2, 3]
    |> map (|x| x * 2)  -- [2, 4, 6]

Import Exposing Bare

modules
import List exposing (map, filter)
map (|x: Int| x + 1) [1, 2, 3]

Indent Correct

modules
module CustomMath exposing (add)

    fn add: Int -> Int -> Int
    fn add x y =
        x + y
...

Indent Error

modules
-- Error: Inline module content must be indented
module CustomMath exposing (add)

fn add x y = x + y    -- Wrong: not indented

Inline

modules
module CustomMath exposing (add, multiply)

    fn add: Int -> Int -> Int
    fn add x y =
        x + y
...

Recursive

modules
module CustomMath exposing (factorial, fibonacci)

    fn factorial: Int -> Int
    fn factorial n =
        if n <= 1 then 1
        else n * factorial (n - 1)
...

Shadowing Math

modules
module Math exposing (double)
    fn double : Int -> Int
    fn double x = x * 2

Math.double 5

Shapes Enum Module

modules
module Shapes exposing (Shape, area)
    type Shape = Circle(Float) | Square(Float)

    fn area : Shape -> Float
    fn area shape = case shape of
        Circle r -> 3.14 * r * r
...

Stdlib Qualified

modules
import List
import String
import Result
import Maybe
import Json
...

Operators

Pattern Matching

As Pattern

pattern-matching
let maybe = Just 42

case maybe of
    Just x -> (x, Just x)
    Nothing -> (0, Nothing)

As Pattern List

pattern-matching
let list =
    [1, 2, 3]

case list of
    x :: xs as all -> "Head"
    [] -> "empty"

Branch Mismatch

pattern-matching
case 1 of
    1 -> 42        -- Int
    2 -> "hello"   -- Error: Case branches have incompatible types
    _ -> 0

Cons

pattern-matching
let list =
    [1, 2, 3]

case list of
    x :: xs -> x  -- x is the head, xs is the tail
    [] -> 0  -- empty list case

Constructor

pattern-matching
let maybeValue = Just 42

case maybeValue of
    Just x -> "Got value"
    Nothing -> "Nothing"

Exhaustive

pattern-matching
type TrafficLight = Red | Yellow | Green

let light = TrafficLight::Yellow

case light of
    TrafficLight::Red -> "stop"
...

Fn Nested

pattern-matching
fn addPairs: (Int, Int) -> (Int, Int) -> (Int, Int)
fn addPairs p1 p2 =
    case (p1, p2) of
        ((x1, y1), (x2, y2)) -> (x1 + x2, y1 + y2)

addPairs (1, 2) (3, 4)  -- (4, 6)

Fn Pattern

pattern-matching
fn fst: (a, b) -> a
fn fst pair =
    case pair of
        (x, _) -> x

fst
...

Guard Exhaustive

pattern-matching
let x = 0

case x of
    n if n > 0 -> "positive"
    n if n < 0 -> "negative"
    _ -> "zero"

Guards

pattern-matching
let number = 42

case number of
    n if n < 0 -> "negative"
    n if n == 0 -> "zero"
    n if n < 10 -> "small positive"
...

Lambda Record

pattern-matching
-- Record destructuring
let describe =
    |{ name: String, age: Int }| name

describe { name = "Bob", age = 25 }

Lambda Tuple

pattern-matching
-- Tuple destructuring
let addPair = |(x: Int, y: Int)| x + y

addPair (3, 4)  -- 7

Let Record

pattern-matching
let user =
    { name = "Alice", age = 30 }

let { name, age } = user

name

Let Tuple

pattern-matching
let point = (10, 20)

let (x, y) = point

x + y

List Exact

pattern-matching
let list =
    [1, 2]

case list of
    [] -> "empty"
    [x] -> "single"
...

Literal

pattern-matching
let x = 1

case x of
    0 -> "zero"
    1 -> "one"
    _ -> "many"

Multi Cons

pattern-matching
let list = [ 1, 2, 3, 4]

case list of
    a :: b :: rest -> a + b
    x :: xs -> x
    [] -> 0

Nested

pattern-matching
import String

let result = Ok (Just 5)

case result of
    Ok (Just x) -> "Success with value: " ++ String.fromInt x
...

Or Binding

pattern-matching
let point = (5, 2)

case point of
    (x, 1) | (x, 2) | (x, 3) -> x  -- x is bound in all alternatives
    _ -> 0

Or Pattern

pattern-matching
type Direction
    = North
    | South
    | East
    | West
...

Qualified Enum

pattern-matching
type Color = Red | Green | Blue

let color = Color::Green

case color of
    Color::Red   -> "fire"
...

Record

pattern-matching
let person =
    { name = "Alice", age = 30 }

case person of
    { name, age } -> name

Record Rest

pattern-matching
let user =
    { name = "Bob", age = 25, email = "bob@example.com" }

case user of
    { name, .. } -> "Hello, " ++ name

Recursive

pattern-matching
fn sum: [Int] -> Int
fn sum numbers =
    case numbers of
        [] -> 0
        x :: xs -> x + sum xs
...

Tuple

pattern-matching
let pair = (3, 0)

case pair of
    (0, 0) -> "origin"
    (x, 0) -> "on x-axis"
    (0, y) -> "on y-axis"
...

Type Mismatch

pattern-matching
case 42 of
    "hello" -> 1  -- Error: Pattern type mismatch: expected Int, but pattern is String
    _ -> 0

Type Unify

pattern-matching
let x = 2

case x of
    1 -> 42  -- Int
    2 -> 3.14  -- Float: compatible, unifies to Float
    _ -> 0

Unreachable

pattern-matching
let x = False

case x of
    _ -> "anything"  -- This matches everything
    True -> "true"  -- Warning: Unreachable pattern

Variable

pattern-matching
let point = (3, 4)

case point of
    (x, y) -> x + y

Wildcard

pattern-matching
let tuple = (42, "ignored")

case tuple of
    (x, _) -> x  -- Ignore second element

Projects

Table

Time

Types

Alias Resolved

types
type alias UserId = Int

let x = 42  -- UserId resolves to Int

let y = x  -- Compatible: UserId is Int
...

Bool Int Error

types
True + 1

Bracket List

types
let numbers: [Int] = [1, 2, 3]
numbers

Bracket List All

types
let numbers: [Int] = [1, 2, 3]
let names: [String] = ["Alice", "Bob"]
let nested: [[Int]] = [[1, 2], [3, 4]]
nested

Case Pattern

types
-- Case expression patterns
case (42, "test") of
    (x, y) -> y

Compound

types
-- List of integers
let numbers =
    [1, 2, 3]
    -- Tuple

let pair = (42, "answer")
...

Dataframe Schemas

typesdataframetype-safety
-- Compile-time schema validation
import DataFrame

-- Create typed data
DataFrame.fromRecords [{ name = "Alice", age = 30 }, { name = "Bob", age = 25 }]
    |> DataFrame.select ["name", "age"]
...

Decimal Type

types
let price = 19.99d
let tax = 0.07d
let total = price + price * tax

total

Generic Pair

types
type Pair a b = Pair(a, b)

let p = Pair::Pair(1, "hello")
p

Generic Tree

types
type Tree a
    = Leaf(a)
    | Node(Tree a, a, Tree a)

let tree: Tree Int = Tree::Node(Tree::Leaf(1), 2, Tree::Node(Tree::Leaf(3), 4, Tree::Leaf(5)))
...

Hof Context

types
-- Higher-order function context
import List 

-- Higher-order function context
[1, 2, 3] |> List.map (|x| x * 2)  -- x inferred as Int from list context

Inference

types
let x = 42  -- Inferred as Int

let name = "Alice"  -- Inferred as String

let ratio = 3.14  -- Inferred as Float
...

Inference Annotation

types
import List 

let empty: List Int = []  -- Needed: can't infer element type
List.length empty

Json Parse Annotation

types
import Json

let data: Result { name: String } String = Json.parse "{\"name\": \"Alice\"}"
data

Lambda Annotation

types
|x| x + 1

|x: Int| x + 1

Lambda Pattern

types
-- Lambda parameter patterns with context
(1, 2)
    |> |(a, b)| a + b

Maybe Type

types
-- norun
type Maybe a = Just(a) | Nothing

Maybe Usage

types
let present = Just 42

let absent = Nothing

case present of
    Just n -> "Got a value"
...

Multiline Enum

types
type Direction
    = North
    | South
    | East
    | West
...

Numeric Promotion

types
let x = 1 + 2.5

let y = 3 * 1.5

x + y

Open Record Alias

types
type alias Person = { name: String, age: Int }
type alias MinPerson = { name: String, age: Int, .. }

let p: MinPerson = { name = "Alice", age = 30, email = "a@b.com" }
p.name

Open Record Dataframe

types
import DataFrame

-- Open record type allows extra fields
type alias MinSchema = { name: String, age: Int, .. }
let data: DataFrame MinSchema = DataFrame.fromRecords [{ name = "Alice", age = 30, email = "alice@example.com" }]
DataFrame.columns data

Parameterized Alias

types
type alias Container a = { value: a }

let intBox =
    { value = 42 }

intBox.value

Parenthesized All

types
let x: Result (Maybe Int) String = Ok (Just 5)
let y: Maybe (List Int) = Just [1, 2, 3]
let z: List (Maybe Int) = [Just 1, Nothing, Just 2]
z

Parenthesized Types

types
let x: Result (Maybe Int) String = Ok (Just 5)
x

Pipe Context

types
-- Pipe operator provides context
5
    |> |x| x + 1

Primitives

types
let x = 42

let pi = 3.14

let active = True
...

Record Type

types
type alias User =
    { id: Int
    , name: String
    , email: String
    , active: Bool
    }
...

Recursive

types
type MyList a = Nil | Cons(a, MyList a)

let nums = MyList::Cons(1, MyList::Cons(2, MyList::Cons(3, MyList::Nil)))
nums

Result Err

types
let failure = Err "something went wrong"

case failure of
    Ok value -> "Success"
    Err msg -> "Error: " ++ msg

Result Ok

types
let success = Ok 42

case success of
    Ok value -> "Success"
    Err msg -> "Error: " ++ msg

Result Type

types
-- norun
type Result a e = Ok(a) | Err(e)

Shape Construction

types
type Shape = Circle(Float) | Rectangle(Float, Float)

Shape::Rectangle(10.0, 20.0)

Simple Enum

types
type Direction = North | South | East | West

let heading = Direction::North
heading

Tuple Destructure

types
-- Tuple destructuring
let pair = (1, "hello")

let (x, y) = pair  -- x: Int, y: String

y

Type Alias

types
type alias Name = String

type alias Age = Int

let userName = "Bob"
...

Type Alias All

types
type alias Person = { name: String, age: Int }

let x: Result Person String = Ok { name = "Alice", age = 30 }
let y: Maybe Person = Just { name = "Bob", age = 25 }
let z: List Person = [{ name = "Alice", age = 30 }]
z

Type Alias Result

types
type alias Person = { name: String, age: Int }

let x: Result Person String = Ok { name = "Alice", age = 30 }
x

Using Enum

types
type Direction = North | South | East | West

let favorite: Direction = Direction::North

case favorite of
    Direction::North -> "Going up"
...

Variant Data Circle

types
type Shape
    = Circle(Float)
    | Rectangle(Float, Float)

fn area : Shape -> Float
fn area shape = case shape of
...

Variant Data Rect

types
type Shape
    = Circle(Float)
    | Rectangle(Float, Float)

fn area : Shape -> Float
fn area shape = case shape of
...

Variant Record

types
type User
    = Guest
    | Member { name: String, id: Int }

let user = User::Member { name = "Alice", id = 42 }
...

Valuelabelset

Variables