Documentation

Standard library

Walnut ships a standard library in three tiers:

TierWhat
BuiltinsBasics, String, List, Tuple, Char, Debug; Just/Nothing, Ok/Err
Core librariesMaybe, Result, Dict, Set, Ui, Cmd, Sub, Walnut, Test
Effect librariesHttp, Time, Storage, Keychain, CoreData, Notifications, Port

Basics is in scope unqualified everywhere. Everything else is always reachable qualified (String.fromInt); import ... exposing adds bare names. See views, effects, and testing.

API reference: walnut docs --stdlib (Homebrew installs the site under share/doc/walnut/api/). Authoring format: documentation.md.

Basics

Always available, no import needed.

-- arithmetic (also the operators + - * / // ^)
toFloat 3            --> 3.0
round 2.5            --> 3         floor 2.9   --> 2
ceiling 2.1          --> 3         truncate -2.9 --> -2
abs -4               --> 4         negate 3    --> -3
sqrt 16.0            --> 4.0       clamp 0 10 42 --> 10
sin / cos / tan / asin / acos / atan   -- Float -> Float
ln / log / log10 / exp                 -- log is natural log (not Debug.log)
modBy 3 7            --> 1         remainderBy 3 -7 --> -1
min 1 2 / max 1 2    -- work on any comparable
pi, e                -- Float constants (compile to literals)

-- logic (also && || not)
not True             --> False
xor True False       --> True

-- comparison
compare 1 2          --> LT        -- Order: LT | EQ | GT

-- functions
identity 5           --> 5
always 1 "ignored"   --> 1
-- plus the operators |> <| >> <<

String

String.fromInt 42            --> "42"
String.fromFloat 2.5         --> "2.5"
String.toInt "42"            --> Just 42
String.toFloat "x"           --> Nothing
String.length "walnut"       --> 6
String.isEmpty ""            --> True
String.reverse "abc"         --> "cba"
String.repeat 3 "na"         --> "nanana"
String.append "wal" "nut"    --> "walnut"     (or ++)
String.concat ["a","b"]      --> "ab"
String.join ", " ["a","b"]   --> "a, b"
String.split "," "a,b"       --> ["a","b"]
String.words " a  b "        --> ["a","b"]
String.lines "a\nb"          --> ["a","b"]
String.toUpper / toLower / trim
String.contains "al" "walnut"    --> True
String.startsWith / endsWith
String.replace "a" "o" "walnut"  --> "wolnut"
String.slice 1 3 "walnut"        --> "al"     (negative indices count from the end)
String.left 3 / right 3 / dropLeft 3 / dropRight 3
String.padLeft 5 '0' "42"        --> "00042"  (and padRight)
String.fromChar 'w'              --> "w"
String.toList / fromList         -- String <-> List Char

List

List.singleton 1                 --> [1]
List.repeat 3 "x"                --> ["x","x","x"]
List.range 1 5                   --> [1,2,3,4,5]
List.length / isEmpty / reverse / member
List.head [1,2]                  --> Just 1
List.tail [1,2]                  --> Just [2]
List.take 2 / drop 2
List.append a b                  -- or ++, and :: to cons
List.concat [[1],[2,3]]          --> [1,2,3]
List.map (\x -> x * 2) [1,2]     --> [2,4]
List.indexedMap Tuple.pair ["a"] --> [(0,"a")]
List.concatMap / filterMap
List.filter (\n -> n > 1) [1,2]  --> [2]
List.foldl (+) 0 [1,2,3]         --> 6        (and foldr)
List.all / any
List.sum / product / maximum / minimum
List.sort [3,1,2]                --> [1,2,3]
List.sortBy .age users
List.intersperse ", " ["a","b"]  --> ["a",", ","b"]
List.map2 (+) [1,2] [10,20]      --> [11,22]

List.foldl steps may return the accumulator unchanged. Returning a borrowed record field without copying can UAF — prefer p.value + 0 when folding fields. Host regression: ListSmoke “foldl max with identity-acc”.

Maybe

Helpers live in Maybe.walnut (Just / Nothing are builtins).

type Maybe a = Just a | Nothing   -- constructors always in scope

Maybe.withDefault 0 (Just 5)     --> 5
Maybe.map    ((+) 1) (Just 1)    --> Just 2
Maybe.andThen                     -- chain functions returning Maybe
Maybe.map2 (+) (Just 1) (Just 2) --> Just 3

Result

Helpers live in Result.walnut (Ok / Err are builtins).

type Result e a = Ok a | Err e    -- constructors always in scope

Result.withDefault 0 (Ok 5)      --> 5
Result.map / map2 / mapError / andThen
Result.toMaybe (Ok 1)            --> Just 1
Result.fromMaybe "missing" Nothing --> Err "missing"

Dict

Association-list dictionaries (Elm-shaped API). Fine for app-scale maps.

import Dict exposing (Dict)

Dict.empty
Dict.singleton "a" 1
Dict.insert "b" 2 dict
Dict.get "a" dict                --> Just 1
Dict.fromList [ { key = 1, value = "x" }, { key = 2, value = "y" } ]
Dict.keys / values / toList / size / member / update / foldl / filter / union

fromList / toList use { key, value } records (tuple destructuring is not fully compiled yet). Prefer Dict.insert when indexing domain records.

Set

Sets as Dict k ().

import Set exposing (Set)

Set.fromList [ "a", "b" ]
Set.member "a" set
Set.union / intersect / diff / map / foldl / filter

Tuple

Tuple.pair 1 2         --> (1, 2)
Tuple.first (1, 2)     --> 1
Tuple.second (1, 2)    --> 2
Tuple.mapFirst / mapSecond

Char

Char.isDigit '7'      --> True
Char.isAlpha / isUpper / isLower
Char.toUpper 'a'      --> 'A'
Char.toCode 'A'       --> 65
Char.fromCode 65      --> 'A'

Debug

Debug.toString { a = 1 }     --> "{ a = 1 }"
Debug.log "model" model      -- prints "model: ..." to the console, returns the value
Debug.todo "not implemented" -- crashes with your message when evaluated

Debug.toString is a runtime prim (wn_debug_to_string) — used by Test.equal. Debug.log output goes to the Xcode console on device/simulator, and to stdout in the REPL and walnut run.

Test

Host JIT tests. import Test exposing (..) then format (run (describe "…" [ test "…" … ])). Full story: testing.md.

Walnut

Walnut.sandbox :
    { init : model, update : msg -> model -> model, view : model -> Ui.View msg }
    -> Walnut.Program model msg

The program constructor. See architecture.

Ui

The view layer — elements, attributes, colors, glass. Catalogued in views, interop helpers in UIKit interop.

Conventions

  • Data structure argument last (List.map fn list), so pipelines flow.
  • Partial functions return Maybe (List.head, String.toInt) — no exceptions, no null.
  • Everything is curried; List.map (\x -> x * 2) is a reusable function.