Documentation
Standard library
Walnut ships a standard library in three tiers:
| Tier | What |
|---|---|
| Builtins | Basics, String, List, Tuple, Char, Debug; Just/Nothing, Ok/Err |
| Core libraries | Maybe, Result, Dict, Set, Ui, Cmd, Sub, Walnut, Test |
| Effect libraries | Http, 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.