Code Examples
Explore Keel through practical code examples. Click any example to open it in the playground.
369 examples
Basics
Comments
-- This is a line comment
{- This is a
block comment -}
let x = 42 -- inline comment...
Doc Comment
{-| Increments a number by one. -}
fn inc : Int -> Int
fn inc x = x + 1
inc 1Functions
fn add: Int -> Int -> Int
fn add x y =
x + y
add 2 3 -- Returns 5If Indentation
let condition = True
if condition then
"result1"
else
"result2" -- Must align with 'result1'Indentation
fn example: Int -> Int
fn example x =
let y = 1 -- Block must be indented
x + y
example 5Inline Module
module CustomMath exposing (add)
fn add: Int -> Int -> Int
fn add x y =
x + y
...
Operators
-- Arithmetic
let sum = 1 + 2
let diff = 5 - 3
let prod = 4 * 2...
Shadowing
let x = 1
let x = x + 1
xValues Types
-- Numbers
let intVal = 42 -- Int
let floatVal = 3.14 -- Float
let price = 19.99d -- Decimal (exact precision)...
Variables
let x = 42
let name = "Alice"
let pi = 3.14159
...
Collections
Bracket List
let xs: [Int] = [1, 2, 3]
let ys: List Int = [4, 5, 6]
ysBuilding Lists
-- Prepend element
let list1 =
1 :: [2, 3]
-- Concatenate lists
let list2 =...
Dataframe Fromrecords
import DataFrame
DataFrame.fromRecords
[ { name = "Alice", score = 95 }
, { name = "Bob", score = 87 }
, { name = "Charlie", score = 92 }...
Filter
-- Filter: keep matching elements
import List
[ 1, 2, 3, 4]
|> List.filter (|x| x > 2)
...
Fold
-- Fold: reduce to single value
import List
[1, 2, 3, 4]
|> List.foldl (|acc x| acc + x) 0
...
Length
import List
fn length: [a] -> Int
fn length list =
case list of
[] -> 0...
List Access
let items =
[10, 20, 30]
items[0]List Ops
-- Prepend (cons)
let consed =
1 :: [2, 3]
-- Concatenation
let joined =...
List Pattern
let list =
[1, 2, 3]
case list of
[] -> "empty"
[x] -> "single element"...
Lists
let numbers =
[ 1
, 2
, 3
, 4
, 5...
Map
-- Map: transform each element
import List exposing (map)
[1, 2, 3]
|> map (|x| x * 2) -- [2, 4, 6]Multiline List
let users =
[ { name = "Alice", age = 30 }
, { name = "Bob", age = 25 }
, { name = "Charlie", age = 35 }
]
usersMultiline Record
let config =
{ host = "localhost"
, port = 8080
, debug = True
, maxConnections = 100
}...
Nested Record
let data =
{ user = { profile = { name = "Alice" } } }
data.user.profile.nameRecord Access
let user =
{ name = "Alice", age = 30 }
user.nameRecord Destructure
let person =
{ name = "David", age = 40 }
let { name, age } = person
nameRecord Multi Update
let person =
{ name = "Alice", age = 30 }
let updated = { person | age = 31, name = "Alicia" }
updated.nameRecord Pattern
let person =
{ name = "Bob", age = 25 }
case person of
{ name, age } -> nameRecord Rest
let user =
{ name = "Carol", age = 35, email = "carol@example.com" }
case user of
{ name, .. } -> "Hello, " ++ nameRecord Update
-- Record update syntax
type alias Person = { name: String, age: Int }
let person =
{ name = "Alice", age = 30 }...
Record Workflow
-- Record update creates a new record
let user = { name = "Eve", age = 28 }
let updatedUser = { user | age = user.age + 1 }
updatedUser.nameRecords
let user =
{ name = "Alice", age = 30, email = "alice@example.com" }
user.nameReverse
import List
fn reverse: [a] -> [a]
fn reverse list =
case list of
[] -> []...
Tuple Access
let pair = (1, "hello")
let first = pair.0 -- 1
let second = pair.1 -- "hello"
...
Tuple Access 2
let triple = (True, 42, "world")
triple.2Tuple Destructure
let (x, y) = (10, 20)
x + yTuple Pattern
let pair = (3, 0)
case pair of
(0, 0) -> "origin"
(x, 0) -> "on x-axis"
(0, y) -> "on y-axis"...
Tuples
let pair = (1, "one")
let triple = (True, 42, "hello")
let point = (3.5, 4.2)
...
Control Flow
Case Enum
type Color = Red | Green | Blue
let color = Color::Green
let description =
case color of...
Catchall
type Color = Red | Green | Blue
let color = Color::Green
case color of
Color::Red -> "primary"...
Else If
let score = 85
let grade =
if score >= 90 then "A"
else if score >= 80 then "B"
else if score >= 70 then "C"...
Exhaustive
type Color = Red | Green | Blue
let color = Color::Blue
case color of
Color::Red -> "red"...
Guard Exhaustive
let x = 0
case x of
n if n > 0 -> "positive"
n if n < 0 -> "negative"
_ -> "zero"Guards
let number = -5
case number of
n if n < 0 -> "negative"
n if n == 0 -> "zero"
n if n > 0 -> "positive"...
If Expression
let x = 5
let result =
if x > 0 then
"positive"
else...
If Multiline
let condition = True
if condition then
"yes"
else
"no"If Same Type
let condition = True
-- Valid: both branches return Int
if condition then
1
else...
If Type Mismatch
if False then 1 else "zero" -- Error: type mismatchList Pattern
let numbers =
[1, 2, 3]
case numbers of
[] -> "empty list"
[x] -> "single element"...
Maybe Handling
let maybeUser = Just "Alice"
let displayName =
case maybeUser of
Just name -> name
Nothing -> "Anonymous"...
Maybe Type
-- norun
type Maybe a = Just(a) | NothingMultiple Head
let list = [1, 2, 3, 4]
case list of
a :: b :: rest -> a + b
x :: xs -> x
[] -> 0Nested Match
let data = (3, 4)
case data of
(a, b) -> a + bOr Patterns
type Day
= Monday
| Tuesday
| Wednesday
| Thursday
| Friday...
Qualified Enum
type Color = Red | Green | Blue
let color = Color::Green
case color of
Color::Red -> "fire"...
Recursive Sum
fn sum: [Int] -> Int
fn sum list =
case list of
[] -> 0
x :: xs -> x + sum xs
...
Result Handling
import String
let parseResult = Ok 42
case parseResult of
Ok n -> "Parsed: " ++ String.fromInt n...
Result Type
-- norun
type Result a e = Ok(a) | Err(e)Dataframe
Expr Aggregation
-- Aggregation with DataFrame.Expr
import DataFrame
import DataFrame.Expr as Expr
DataFrame.fromRecords
[ { group = "A", value = 10 }...
Expr Arithmetic
-- Arithmetic expressions: column * column
import DataFrame
import DataFrame.Expr as Expr
DataFrame.fromRecords
[ { price = 10, quantity = 3 }...
Expr Basics
-- DataFrame.Expr for composable column operations
import DataFrame
import DataFrame.Expr as Expr
DataFrame.fromRecords
[ { name = "Alice", revenue = 100 }...
Expr Comparison
-- Comparison expressions: column >= literal
import DataFrame
import DataFrame.Expr as Expr
DataFrame.fromRecords
[ { name = "Alice", age = 25 }...
Expr Conditional
-- Conditional expressions with DataFrame.Expr
import DataFrame
import DataFrame.Expr exposing (col, lit)
import DataFrame.Expr as Expr
DataFrame.fromRecords...
Expr Math
-- Math functions: abs
import DataFrame
import DataFrame.Expr as Expr
DataFrame.fromRecords
[ { value = -3.7 }...
Expr Null
-- Null handling: fillNull
import DataFrame
import DataFrame.Expr as Expr
DataFrame.fromRecords
[ { score = 85 }...
Expr String Ops
-- String operations with DataFrame.Expr
import DataFrame
import DataFrame.Expr as Expr
DataFrame.fromRecords
[ { name = "alice" }...
Expr Window
import DataFrame
import DataFrame.Expr as Expr
let df = DataFrame.fromRecords
[ { region = "East", revenue = 100 }
, { region = "East", revenue = 200 }...
Lineage Agg Origin
-- norun
-- Aggregated columns track source and function
import DataFrame
let df =
DataFrame.fromRecords...
Lineage Auto
-- norun
-- Lineage appears automatically when printing a DataFrame
import DataFrame
let sales =
DataFrame.fromRecords...
Lineage Column
-- norun
-- DataFrame.columnLineage returns origin info for a column
import DataFrame
let df =
DataFrame.fromRecords...
Lineage Dag Join
-- Join produces two parents in the DAG
import DataFrame
import List
let users =
DataFrame.fromRecords...
Lineage Full Record
-- norun
-- Full lineage record structure
import DataFrame
let df =
DataFrame.fromRecords...
Lineage Global Ops
-- norun
-- Global operations (filter, sort) are tracked separately
import DataFrame
let df =
DataFrame.fromRecords...
Lineage Join Origin
-- norun
-- Joined columns track their source DataFrame
import DataFrame
let users =
DataFrame.fromRecords...
Lineage Parents Derived
-- Derived DataFrames track parent operations
import DataFrame
import List
let df =
DataFrame.fromRecords...
Lineage Parents Root
-- Root DataFrames have no parents
import DataFrame
let df =
DataFrame.fromRecords
[ { name = "Alice", age = 30 }...
Lineage Rename Origin
-- norun
-- After rename, the transformation tracks the operation
import DataFrame
let df =
DataFrame.fromRecords...
Lineage Sourcepath
-- DataFrame.sourcePath returns Nothing for fromRecords
import DataFrame
let df =
DataFrame.fromRecords
[ { name = "Alice", age = 30 }...
Lineage Tracking
-- 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
-- norun
-- Columns track their transformation history
import DataFrame
let df =
DataFrame.fromRecords...
Recode
-- Remap values with automatic label transfer
import DataFrame
let df =
DataFrame.fromRecords
[ { id = 1, score = 1 }...
Value Labels
-- Attach value labels for coded variables
import DataFrame
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
...
Variable Labels
-- Variable labels for column descriptions
import DataFrame
let df =
DataFrame.fromRecords
[ { id = 1, gender = 1 }...
Window 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
Arithmetic
-- Date arithmetic
import Date
let date = case Date.fromYmd 2024 3 15 of
Ok d -> d
Err _ -> Date.today ()...
Comparison
-- Comparing dates and computing differences
import Date
let start = case Date.fromYmd 2024 1 1 of
Ok d -> d
Err _ -> Date.today ()...
Components
-- Extracting date components
import Date
let date = case Date.fromYmd 2024 3 15 of
Ok d -> d
Err _ -> Date.today ()...
Create
-- Creating dates from year, month, day
import Date
Date.fromYmd 2024 3 15Error Handling
import Date
-- Invalid date returns a typed error
case Date.fromYmd 2024 2 30 of
Ok date -> Date.toIsoString date
Err _ -> "Invalid day for this month"Parse
-- Parsing dates from strings
import Date
Date.parseIso "2024-06-15"Parse Custom
import Date
-- Custom format
Date.parse "15/06/2024" "%d/%m/%Y"Datetime
Interop Combine
-- Combine Date and Time into DateTime
import DateTime
import Date
import Time
let date = case Date.fromYmd 2024 6 15 of...
Interop Fromdate
-- Convert Date to DateTime at midnight UTC
import DateTime
import Date
let date = case Date.fromYmd 2024 1 1 of
Ok d -> d...
Interop Getdate
-- Extract Date component from DateTime
import DateTime
import Date
let dt = case DateTime.fromDate 2024 6 15 of
Just d -> d...
Interop Gettime
-- Extract Time component from DateTime
import DateTime
import Time
let dt = case DateTime.fromParts 2024 6 15 14 30 0 of
Just d -> d...
Decimal
Comparison
-- Decimal comparison and checks
import Decimal
let a = 10.5d
let b = 3.2d
...
Conversion
-- Converting between Decimal and other types
import Decimal
let price = 19.99d
-- Convert to string...
Literals
-- Decimal literals use the 'd' suffix
import Decimal
let price = 19.99d
let taxRate = 0.073d
...
Parsing
-- Parse a string into a Decimal
import Decimal
Decimal.parse "42.5"Rounding
-- Rounding decimals to specific precision
import Decimal
let price = 19.99d
let taxRate = 0.073d
let total = price + price * taxRate...
Duration
Arithmetic
-- Duration arithmetic
import Duration
let oneHour = Duration.fromHours 1
let thirtyMin = Duration.fromMinutes 30
...
Comparison
-- Duration comparison and checks
import Duration
let d = Duration.fromMinutes 90
Duration.isPositive dConversion
-- Converting between duration units
import Duration
let d = Duration.fromMinutes 150
-- Get total hours (truncated)...
Create
-- Creating and converting durations
import Duration
let twoHours = Duration.fromHours 2
Duration.toSeconds twoHoursError Handling
Arg Type Map
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: IntArg Type String
import String
String.length 42
-- Error: Type mismatch: expected String, found Int
-- Hint: Function signature: String -> Int
-- Expected argument type: String, but got: IntBlock Align
if True then
0
else -- Error: 'else' must align with 'if'
1Block Nest
let x =
x + 1 -- Error: Block must be indented more than parent
-- Hint: Indent the block to 4 spacesBool Correct
let x = True
let y = False
x && yBool Error
let x = true -- Error: 'true' is not a Keel keyword
-- Hint: Use `True` for boolean trueBounds Error
let items =
[1, 2, 3]
items[10] -- Error: Index 10 out of bounds for list of size 3Bounds Safe
let items =
[1, 2, 3]
items[0] -- 1Branch Correct
let x = 2
case x of
1 -> "one"
2 -> "two"
_ -> "other"Branch Mismatch
let x = 2
case x of
1 -> 42 -- Int
2 -> "hello" -- Error: Case branches have incompatible types: Int and String
_ -> 0Constructor Type
case Just 5 of
Ok n -> n -- Error: Pattern type mismatch: expected Maybe, but pattern is Result (Ok)
_ -> 0Converting Types
import Result
Err "x" |> Result.toMaybeConverting Types
import Result
-- Maybe to Result (with error message)
let a = Just 5 |> Result.fromMaybe "was nothing"
-- Result to Maybe (discards error)...
Correct Syntax
fn double: Int -> Int
fn double x =
x * 2
-- Pattern matching uses case...of
let value = Just 5...
Enum Case
type Direction = North | south -- Error: Enum variant must start with uppercaseFuzzy Multiple
import IO exposing (print)
let userName = "Alice"
let userAge = 30
let userEmail = "alice@example.com"
...
Fuzzy Single
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
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 typeGuard Correct
let x = 5
case x of
n if n > 0 -> "positive"
n if n < 0 -> "negative"
_ -> "zero"Guard Type
let x = 2
case x of
n if n + 1 -> "bad" -- Error: Guard expression must be Bool, found Int
_ -> "ok"Indent Error
fn example : Int -> Int
fn example x =
let y = 1 -- Error: Function body must be indented by at least 4 spaces
x + yJs Record
{ 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
import Json
let data: Result { x: Int } String = Json.parse "{\"x\": 1}"
dataKeywords
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 bindingsMaybe Correct
let present = Just 42
let absent = Nothing
case present of
Just n -> "Got a value"...
Maybe Module
import Maybe
Nothing |> Maybe.withDefault 0Maybe Module
import Maybe
-- Transform present values
let mapped = Just 5 |> Maybe.map (|x| x * 2)
-- Chain operations that might return Nothing...
Maybe Types
-- Safe error handling with Maybe
import List
import Maybe
-- Extract values with default fallback
[Just 42, Nothing, Just 7]...
Mismatched Delim
let x = [1, 2)
-- Error: Mismatched delimiter — '(' expected ')' but found ')'Module Fn Typo
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
let x = null -- Error: 'null' is not a Keel keyword
-- Hint: Use `Nothing` for absent valuesPattern Type
case 42 of
"hello" -> 1 -- Error: Pattern type mismatch: expected Int, but pattern is String
_ -> 0Record Correct
{ name = "Alice", age = 30 }Result Module
import Result
Err "bad" |> Result.mapError (|e| "Error: " ++ e)Result Module
import Result
-- Transform success values
let mapped = Ok 5 |> Result.map (|x| x * 2)
-- Chain operations that might fail...
Scope
fn example : Int -> Int
fn example x =
let inner = x + 1
inner
inner -- Error: Variable 'inner' is not in scopeTuple Annotation
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
fn add: Int -> Int -> Int
fn add x y =
x + y
add "hello" 5 -- Error: Expected Int but got StringType Vs Alias
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
let x = (1 + 2
-- Error: Unclosed '(' — expected ')'Undeclared
let x = unknownVar -- Error: Variable 'unknownVar' is not declaredUnexpected Delim
let x = 1 + 2]
-- Error: Unexpected ']' — no matching '['Variant Parens
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
module CustomMath exposing (..)
fn add: Int -> Int
fn add x =
x + 1
...
Arity Overloading 2
module CustomMath exposing (..)
fn add: Int -> Int
fn add x =
x + 1
...
Common Filter
import List
-- filter: keep matching elements
[1, 2, 3, 4] |> List.filter (|x| x > 2)Common Fold
import List
-- fold: reduce to single value
[1, 2, 3, 4] |> List.foldl (|acc x| acc + x) 0Common Map
import List
-- map: transform each element
[1, 2, 3] |> List.map (|x| x * 2)Composition
fn double : Int -> Int
fn double x = x * 2
fn addOne : Int -> Int
fn addOne x = x + 1
...
Currying
fn add : Int -> Int -> Int
fn add x y = x + y
let add5 = add 5
add5 3Defining
fn greet: String -> String
fn greet name =
"Hello, " ++ name ++ "!"
greet "Alice" -- "Hello, Alice!"Duplicate Sig
fn add : Int -> Int
fn add x = x + 1
fn add : Int -> Int
fn add x = x + 2Factorial
module CustomMath exposing (factorial)
fn factorial: Int -> Int
fn factorial n = if n <= 1 then 1 else n * factorial (n - 1)
CustomMath.factorial 5Factorial Case
fn factorial: Int -> Int
fn factorial n =
case n of
0 -> 1
1 -> 1
_ -> n * factorial (n - 1)...
Fibonacci
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 10Generic Assign
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
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
fn identity : a -> a
fn identity x = x
identity 42Higher Order
-- Takes a function as argument
fn applyTwice: (Int -> Int) -> Int -> Int
fn applyTwice f x =
f (f x)
applyTwice (|x: Int| x + 1) 5 -- 7Higher Order Context
module M exposing (..)
fn apply: (Int -> Int) -> Int -> Int
fn apply f x =
f x
...
Lambda As Pattern
let withAlias = |x as m: Int| (x, m)
withAlias 42Lambda Basics
let addOne = |x: Int| x + 1
let addTwo = |x: Int y: Int| x + y
let addTuple = |(x: Int, y: Int)| x + y
addOne 5Lambda Error
|x| x + 1Lambda Map
import List
[1, 2, 3] |> List.map (|x| x * 2)Lambda Multiline
let compute = |x: Int|
let y = 3
x + y
compute 7Lambda No Annotation
|x| x + 1Lambda Record Fields
let describe = |{ name: String, age: Int }| name
describe { name = "Alice", age = 30 }Lambda Rest Pattern
let getName = |{ name: String, .. }| name
getName { name = "Alice", age = 30 }Lambda Tuple Add
let addPair = |(x: Int, y: Int)| x + y
addPair (3, 4)Lambda Typed
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
let f = |x: Int| x + 1
f 1Local Bindings
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
fn makeAdder : Int -> (Int -> Int)
fn makeAdder n = |x: Int| x + n
makeAdder 10 5Native Pipes
import String
"hello" |> String.toUpper |> String.lengthNative Pipes
import String
import List
import Math
let a = "hello" |> String.toUpper |> String.length
let b = "hello" |> String.slice 0 3 |> String.toUpper...
Overloaded Hof
module Apply exposing (..)
fn apply: (Int -> Int) -> Int -> Int
fn apply f x =
f x
...
Overloading Int
module CustomMath exposing (..)
fn double: Int -> Int
fn double x =
x * 2
...
Overloading String
module CustomMath exposing (..)
fn double: Int -> Int
fn double x =
x * 2
...
Overloading Toplevel
fn inc: Int -> Int
fn inc x =
x + 1
fn inc: Float -> Float
fn inc x =...
Pattern Matching Fn
fn length : [a] -> Int
fn length list =
case list of
[] -> 0
x :: xs -> 1 + length xs
...
Pipe Chain
fn double: Int -> Int
fn double x =
x * 2
fn addOne: Int -> Int
fn addOne x =...
Pipe Operator
-- Pipe operator provides context from the left operand
5
|> |x| x + 1Pipeline Example
import List
[1, 2, 3, 4, 5]
|> List.filter (|x| x > 2)
|> List.map (|x| x * 2)Record Field Pipe
-- Mixed typed and untyped fields (untyped inferred from context)
{ x = 1, y = 2 } |> |{ x: Int, y }| x + yStdlib Chained Inference
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
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
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
import List
-- Lambda parameter type inferred from List.map signature
List.map (|x| x * 2)[1, 2, 3]
-- [2, 4, 6]Stdlib Zipwith Inference
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
let pair = (42, "hello")
let (x, _) = pair
xTuple Arg Fst
fn fst : (Int, String) -> Int
fn fst pair =
case pair of
(x, _) -> x
fst (42, "hello")Tuple Arg Swap
let pair = (1, "hello")
let (x, y) = pair
(y, x)Tuple Arg Swap
fn swap : (a, b) -> (b, a)
fn swap pair =
case pair of
(x, y) -> (y, x)
swap (1, "hello")Type Signatures
fn add: Int -> Int -> Int
fn add x y =
x + y
add 3 4 -- 7Getting Started
Http
Indentation
Basic Blocks
-- Function body must be indented
fn addOne : Int -> Int
fn addOne x =
x + 1
addOne 5 -- 6Block Nest Correct
let x =
1 + 2 + 3
xBlock Nest Fn Call
let greeting =
"hello"
greetingBlock Nest Multi Let
let a =
5
let b =
10
a + bBlock Nest Multiline
let result =
1 + 2 + 3Block Nest Nested
let x =
let y = 3
let z = 4
y + z
xBlock Nest String Expr
let greeting =
"hello"Block Nest Toplevel
let x =
x + 1Case Indentation
-- Case branches must be indented from 'case'
let value = 2
let result = case value of
1 -> "one"
2 -> "two"...
Case Multiline Body
-- Case branch bodies can span multiple lines
let value = 2
let result = case value of
1 ->
let x = 10...
Continuation Argument
-- 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
-- If-then-else with multiline expressions
let condition = True
let result =
if condition then
1 + 2...
Let Block
-- 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
let x =Let Missing Expr Eof
let a = 10
let b =List Multiline
-- Lists can span multiple lines (Elm-style)
import List
let numbers =
[ 1
, 2...
Local Fn Args
fn add : Int -> Int -> Int
fn add x y = x + y
let result = add 1 2
resultMultiline Function Call
-- 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
import List
List.map (|x| x * 2)
[1, 2, 3]Pipe Multiline
-- 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
-- Records can span multiple lines (Elm-style)
let person =
{ name = "Alice"
, age = 30
, city = "Paris"
}...
Scope Nested
-- 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
fn example : Int -> Int
fn example n =
let a = n + 1
let b = a * 2
let c = a + b
c...
Separate Expressions
-- 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
Compile Time Safety
-- Type-safe column operations validated at compile time
import DataFrame
import DataFrame.Expr as Expr
let sales = DataFrame.readParquet "public/data/sales.parquet"
...
Compliance
-- Built-in audit trails for regulatory compliance
import DataFrame
let payments = DataFrame.readParquet "public/data/payments.parquet"
let auditReport = payments...
Data Contracts
-- Data contracts: schema enforced by the compiler, not documentation
import DataFrame
type alias Schema =
{ customer_id: Int
, name: String...
Data Lineage
-- Automatic data lineage tracking
import DataFrame
import DataFrame.Expr as Expr
let transactions = DataFrame.readParquet "public/data/transactions.parquet"
...
Lexical
Bool Literals
TrueChar Literals
'a'Decimal Literals
42d -- Integer decimal
3.14d -- Fractional decimalFloat Literals
3.14Indentation
fn example: Int -> Int
fn example x =
let y = 1 -- Must be indented
x + y
example 1Int Literals
42 -- DecimalLine Comments
-- This is a line comment
let x = 42 -- Inline comment
xRecord Syntax
{ name = "Alice", age = 30 }String Literals
"Hello, World!"Unit Literal
()Modules
Access Add
module CustomMath exposing (add, multiply)
fn add: Int -> Int -> Int
fn add x y =
x + y
...
Access Multiply
module CustomMath exposing (multiply)
fn multiply: Int -> Int -> Int
fn multiply x y =
x * y
...
Exposing
-- norun
-- Expose specific items
module exposing (functionA, functionB, TypeA)
-- Expose all
module exposing (..)...
Fibonacci
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
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
import Math
module Geometry exposing (Shape, area, perimeter)
type Shape = Circle(Float) | Rectangle(Float, Float) | Triangle(Float, Float, Float)
...
Geometry Perimeter
module Geometry exposing (perimeter)
fn perimeter: Float -> Float
fn perimeter r =
2.0 * 3.14159 * r
...
Import
import StringImport Alias
import Math as SomethingImport Alias Exposing
import List as L exposing (length)
length [1, 2]Import Aliases
-- Modules and import aliases
import Math as M
import List as L
M.abs (L.length [1, 2, 3])Import Exposing
import List exposing (map)
-- Using built-in list functions directly
[1, 2, 3]
|> map (|x| x * 2) -- [2, 4, 6]Import Exposing Bare
import List exposing (map, filter)
map (|x: Int| x + 1) [1, 2, 3]Indent Correct
module CustomMath exposing (add)
fn add: Int -> Int -> Int
fn add x y =
x + y
...
Indent Error
-- Error: Inline module content must be indented
module CustomMath exposing (add)
fn add x y = x + y -- Wrong: not indentedInline
module CustomMath exposing (add, multiply)
fn add: Int -> Int -> Int
fn add x y =
x + y
...
Recursive
module CustomMath exposing (factorial, fibonacci)
fn factorial: Int -> Int
fn factorial n =
if n <= 1 then 1
else n * factorial (n - 1)...
Shadowing Math
module Math exposing (double)
fn double : Int -> Int
fn double x = x * 2
Math.double 5Shapes Enum Module
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
import List
import String
import Result
import Maybe
import Json
...
Operators
Arithmetic
let addition = 5 + 3
let subtraction = 10 - 4
let multiply = 6 * 7
...
Comparison
let eq = 5 == 5
let neq = 5 != 6
let lt = 3 < 5
...
Compose Backward
-- Backward composition (g first, then f)
let addOne = |x: Int| x + 1
let double = |x: Int| x * 2
let doubleThenAdd = addOne << double...
Compose Forward
-- Forward composition (f first, then g)
let addOne = |x: Int| x + 1
let double = |x: Int| x * 2
let addThenDouble = addOne >> double...
Division
let floatDiv = 10 / 3
let intDiv = 10 // 3
(floatDiv, intDiv)List Ops
-- Cons (prepend element)
let consed1 =
1 :: [2, 3]
let consed2 : List Int = 1 :: (2 :: [])
...
Logical
-- And (both must be True)
let and1 = True && True
let and2 = True && False
-- Or (at least one must be True)...
Pipe Forward
fn double: Int -> Int
fn double x =
x * 2
fn addOne: Int -> Int
fn addOne x =...
Pipeline
import List
-- Pipeline example
[1, 2, 3, 4, 5]
|> List.filter (|x| x > 2)
|> List.map (|x| x * 2)...
Short Circuit
-- False && ... returns False immediately without evaluating the right side
let result = False && True
result -- FalseString Concat
"Hello, " ++ "World!"Type Promotion
let x = 1 + 2.5
let y = 3 * 1.5
x + yPattern Matching
As Pattern
let maybe = Just 42
case maybe of
Just x -> (x, Just x)
Nothing -> (0, Nothing)As Pattern List
let list =
[1, 2, 3]
case list of
x :: xs as all -> "Head"
[] -> "empty"Branch Mismatch
case 1 of
1 -> 42 -- Int
2 -> "hello" -- Error: Case branches have incompatible types
_ -> 0Cons
let list =
[1, 2, 3]
case list of
x :: xs -> x -- x is the head, xs is the tail
[] -> 0 -- empty list caseConstructor
let maybeValue = Just 42
case maybeValue of
Just x -> "Got value"
Nothing -> "Nothing"Exhaustive
type TrafficLight = Red | Yellow | Green
let light = TrafficLight::Yellow
case light of
TrafficLight::Red -> "stop"...
Fn Nested
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
fn fst: (a, b) -> a
fn fst pair =
case pair of
(x, _) -> x
fst...
Guard Exhaustive
let x = 0
case x of
n if n > 0 -> "positive"
n if n < 0 -> "negative"
_ -> "zero"Guards
let number = 42
case number of
n if n < 0 -> "negative"
n if n == 0 -> "zero"
n if n < 10 -> "small positive"...
Lambda Record
-- Record destructuring
let describe =
|{ name: String, age: Int }| name
describe { name = "Bob", age = 25 }Lambda Tuple
-- Tuple destructuring
let addPair = |(x: Int, y: Int)| x + y
addPair (3, 4) -- 7Let Record
let user =
{ name = "Alice", age = 30 }
let { name, age } = user
nameLet Tuple
let point = (10, 20)
let (x, y) = point
x + yList Exact
let list =
[1, 2]
case list of
[] -> "empty"
[x] -> "single"...
Literal
let x = 1
case x of
0 -> "zero"
1 -> "one"
_ -> "many"Multi Cons
let list = [ 1, 2, 3, 4]
case list of
a :: b :: rest -> a + b
x :: xs -> x
[] -> 0Nested
import String
let result = Ok (Just 5)
case result of
Ok (Just x) -> "Success with value: " ++ String.fromInt x...
Or Binding
let point = (5, 2)
case point of
(x, 1) | (x, 2) | (x, 3) -> x -- x is bound in all alternatives
_ -> 0Or Pattern
type Direction
= North
| South
| East
| West
...
Qualified Enum
type Color = Red | Green | Blue
let color = Color::Green
case color of
Color::Red -> "fire"...
Record
let person =
{ name = "Alice", age = 30 }
case person of
{ name, age } -> nameRecord Rest
let user =
{ name = "Bob", age = 25, email = "bob@example.com" }
case user of
{ name, .. } -> "Hello, " ++ nameRecursive
fn sum: [Int] -> Int
fn sum numbers =
case numbers of
[] -> 0
x :: xs -> x + sum xs
...
Tuple
let pair = (3, 0)
case pair of
(0, 0) -> "origin"
(x, 0) -> "on x-axis"
(0, y) -> "on y-axis"...
Type Mismatch
case 42 of
"hello" -> 1 -- Error: Pattern type mismatch: expected Int, but pattern is String
_ -> 0Type Unify
let x = 2
case x of
1 -> 42 -- Int
2 -> 3.14 -- Float: compatible, unifies to Float
_ -> 0Unreachable
let x = False
case x of
_ -> "anything" -- This matches everything
True -> "true" -- Warning: Unreachable patternVariable
let point = (3, 4)
case point of
(x, y) -> x + yWildcard
let tuple = (42, "ignored")
case tuple of
(x, _) -> x -- Ignore second elementProjects
Table
Builder
-- Builder pattern for custom tables
import DataFrame
import Table
DataFrame.fromRecords
[ { region = "North", sales = 100 }...
Cross
-- Two-way cross-tabulation
import DataFrame
import Table
DataFrame.fromRecords
[ { gender = "M", group = "A" }...
Freq
-- One-way frequency table
import DataFrame
import Table
DataFrame.fromRecords
[ { category = "A" }...
Summary Stats
-- Summary statistics by group
import DataFrame
import Table
DataFrame.fromRecords
[ { region = "North", sales = 100 }...
Time
Arithmetic
-- Time arithmetic
import Time
let t = case Time.fromHms 14 30 0 of
Ok t -> t
Err _ -> Time.midnight ()...
Components
-- Extracting time components
import Time
let t = case Time.fromHms 14 30 0 of
Ok t -> t
Err _ -> Time.midnight ()...
Create
-- Creating time values
import Time
Time.fromHms 14 30 0Parse
-- Parsing time from strings
import Time
Time.parseIso "09:15:30"Types
Alias Resolved
type alias UserId = Int
let x = 42 -- UserId resolves to Int
let y = x -- Compatible: UserId is Int
...
Bool Int Error
True + 1Bracket List
let numbers: [Int] = [1, 2, 3]
numbersBracket List All
let numbers: [Int] = [1, 2, 3]
let names: [String] = ["Alice", "Bob"]
let nested: [[Int]] = [[1, 2], [3, 4]]
nestedCase Pattern
-- Case expression patterns
case (42, "test") of
(x, y) -> yCompound
-- List of integers
let numbers =
[1, 2, 3]
-- Tuple
let pair = (42, "answer")...
Dataframe Schemas
-- 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
let price = 19.99d
let tax = 0.07d
let total = price + price * tax
totalGeneric Pair
type Pair a b = Pair(a, b)
let p = Pair::Pair(1, "hello")
pGeneric Tree
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
-- Higher-order function context
import List
-- Higher-order function context
[1, 2, 3] |> List.map (|x| x * 2) -- x inferred as Int from list contextInference
let x = 42 -- Inferred as Int
let name = "Alice" -- Inferred as String
let ratio = 3.14 -- Inferred as Float
...
Inference Annotation
import List
let empty: List Int = [] -- Needed: can't infer element type
List.length emptyJson Parse Annotation
import Json
let data: Result { name: String } String = Json.parse "{\"name\": \"Alice\"}"
dataLambda Annotation
|x| x + 1
|x: Int| x + 1Lambda Pattern
-- Lambda parameter patterns with context
(1, 2)
|> |(a, b)| a + bMaybe Type
-- norun
type Maybe a = Just(a) | NothingMaybe Usage
let present = Just 42
let absent = Nothing
case present of
Just n -> "Got a value"...
Multiline Enum
type Direction
= North
| South
| East
| West
...
Numeric Promotion
let x = 1 + 2.5
let y = 3 * 1.5
x + yOpen Record Alias
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.nameOpen Record Dataframe
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 dataParameterized Alias
type alias Container a = { value: a }
let intBox =
{ value = 42 }
intBox.valueParenthesized All
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]
zParenthesized Types
let x: Result (Maybe Int) String = Ok (Just 5)
xPipe Context
-- Pipe operator provides context
5
|> |x| x + 1Primitives
let x = 42
let pi = 3.14
let active = True
...
Record Type
type alias User =
{ id: Int
, name: String
, email: String
, active: Bool
}...
Recursive
type MyList a = Nil | Cons(a, MyList a)
let nums = MyList::Cons(1, MyList::Cons(2, MyList::Cons(3, MyList::Nil)))
numsResult Err
let failure = Err "something went wrong"
case failure of
Ok value -> "Success"
Err msg -> "Error: " ++ msgResult Ok
let success = Ok 42
case success of
Ok value -> "Success"
Err msg -> "Error: " ++ msgResult Type
-- norun
type Result a e = Ok(a) | Err(e)Shape Construction
type Shape = Circle(Float) | Rectangle(Float, Float)
Shape::Rectangle(10.0, 20.0)Simple Enum
type Direction = North | South | East | West
let heading = Direction::North
headingTuple Destructure
-- Tuple destructuring
let pair = (1, "hello")
let (x, y) = pair -- x: Int, y: String
yType Alias
type alias Name = String
type alias Age = Int
let userName = "Bob"
...
Type Alias All
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 }]
zType Alias Result
type alias Person = { name: String, age: Int }
let x: Result Person String = Ok { name = "Alice", age = 30 }
xUsing Enum
type Direction = North | South | East | West
let favorite: Direction = Direction::North
case favorite of
Direction::North -> "Going up"...
Variant Data Circle
type Shape
= Circle(Float)
| Rectangle(Float, Float)
fn area : Shape -> Float
fn area shape = case shape of...
Variant Data Rect
type Shape
= Circle(Float)
| Rectangle(Float, Float)
fn area : Shape -> Float
fn area shape = case shape of...
Variant Record
type User
= Guest
| Member { name: String, id: Int }
let user = User::Member { name = "Alice", id = 42 }
...
Valuelabelset
Coverage
-- Check which values lack labels
import ValueLabelSet
let labels = ValueLabelSet.fromList [(1, "Agree"), (2, "Disagree")]
let result = ValueLabelSet.coverage [1, 2, 3] labels
result.unlabeledCreate Lookup
-- Create a value label set and look up labels
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
ValueLabelSet.getLabel 1 genderMerge Filter
-- Merge two label sets and check size
import ValueLabelSet
let base = ValueLabelSet.fromList [(1, "Low"), (2, "Medium")]
let extra = ValueLabelSet.fromList [(3, "High")]
...
Reverse Lookup
-- Reverse lookup: find the code for a label
import ValueLabelSet
let gender = ValueLabelSet.fromList [(1, "Male"), (2, "Female")]
ValueLabelSet.getValue "Female" genderVariables
Data Transformation
Declaring
let name = "Alice"
let age = 30
let pi = 3.14159
...
Immutability
Let Expressions
let area =
let width = 10
let height = 5
width * height
area -- 50Multiple Bindings
let x = 1
let y = 2
let z = 3
...
Nested Destructuring
let (x, { a, b }) =
(1, { a = 2, b = 3 })
a -- 2Record Destructuring
let { name, age } =
{ name = "Bob", age = 25 }
name -- "Bob"Scope
fn example : Int -> Int
fn example x =
let inner = x + 1
inner
inner -- Error: `inner` is not in scopeShadowing
let x = 10
let x = x + 5
x -- 15Shadowing Type Change
let x = 1
let x = x + 1
let x = "hello" -- x can change type when shadowed
...
Tuple Destructuring
let (x, y) = (10, 20)
x -- 10Type Annotations
let count = 42
let message = "Hello"
let ratio = 0.75
...