Esc
Start typing to search...

Collections

Keel provides several built-in collection types for grouping and organizing data.

Lists

Lists are ordered, homogeneous collections:

let numbers =
    [ 1
    , 2
    , 3
    , 4
    , 5
    ]

let names =
    ["Alice", "Bob", "Charlie"]

let empty : List Int = []

numbers
Try it

List types can be written as List Int or with bracket syntax [Int]:

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

Multi-line Lists

Lists support Elm-style multi-line syntax with leading commas. This is especially useful for long lists or when passing data directly to functions:

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

Pass directly to functions without intermediate bindings:

import DataFrame

DataFrame.fromRecords
    [ { name = "Alice", score = 95 }
    , { name = "Bob", score = 87 }
    , { name = "Charlie", score = 92 }
    ]
    |> DataFrame.columns
Try it

Multi-line literals must be indented, with commas aligned at the same indentation level.

List Operations

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

let joined =
    [1, 2] ++ [3, 4]

joined
Try it

List Access

Use bracket notation for index access:

let items =
    [10, 20, 30]

items[0]
Try it

List Pattern Matching

let list =
    [1, 2, 3]

case list of
    [] -> "empty"
    [x] -> "single element"
    x :: xs -> "has multiple elements"
Try it

Common List Functions

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

[1, 2, 3]
    |> map (|x| x * 2)  -- [2, 4, 6]
Try it
-- Filter: keep matching elements
import List

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

-- [3, 4]
Try it
-- Fold: reduce to single value
import List

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

-- 10
Try it

Tuples

Fixed-size, heterogeneous collections:

let pair = (1, "one")

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

let point = (3.5, 4.2)

pair
Try it

Accessing Tuple Elements

Use numeric indices with dot notation:

let pair = (1, "hello")

let first = pair.0  -- 1

let second = pair.1  -- "hello"

first
Try it
let triple = (True, 42, "world")

triple.2
Try it

Tuple Destructuring

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

x + y
Try it

Tuple Patterns

let pair = (3, 0)

case pair of
    (0, 0) -> "origin"
    (x, 0) -> "on x-axis"
    (0, y) -> "on y-axis"
    (x, y) -> "somewhere else"
Try it

Records

Named field collections:

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

user.name
Try it

Multi-line Records

Records also support multi-line syntax with leading commas:

let config =
    { host = "localhost"
    , port = 8080
    , debug = True
    , maxConnections = 100
    }
config
Try it

Record Access

Use dot notation for field access:

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

user.name
Try it

Nested record access:

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

data.user.profile.name
Try it

Record Update

Create a new record with some fields changed using the | syntax:

-- Record update syntax

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

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

let older = { person | age = 31 }

older.name
Try it

Multiple field updates:

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

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

updated.name
Try it

Record Patterns

Match record fields:

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

case person of
    { name, age } -> name
Try it

With rest pattern (..) to ignore remaining fields:

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

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

Partial record patterns without .. are an error — this ensures you explicitly acknowledge ignored fields.

Record Destructuring

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

let { name, age } = person

name
Try it

Choosing the Right Collection

CollectionUse When
ListSequential data, pattern matching on head/tail
TupleFixed number of mixed-type values
RecordNamed fields, structured data

Common Patterns

Building Lists

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

let list2 =
    [1, 2] ++ [3, 4]

list2
Try it

Processing Lists

import List

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

length [1, 2, 3, 4, 5]  -- 5
Try it
import List

fn reverse: [a] -> [a]
fn reverse list =
    case list of
        [] -> []
        x :: xs ->
            List.reverse xs ++ [x]

reverse [1, 2, 3]  -- [3, 2, 1]
Try it

Working with Records

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

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

updatedUser.name
Try it

Next Steps

Learn about types to define your own data structures.