Static types everywhere
Messages, models, and views are ordinary typed data. No hidden widget trees, no mystery runtime crashes from mistyped updates. If the compiler accepts it, the app works.
Typed functional language for iOS
Walnut compiles init / update / view programs to native code through LLVM. Static types catch bugs before the app runs. The language is small enough to learn in a day, but UIKit and ports let you reach anything on the platform.
type alias Model =
{ count : Int }
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
{ model | count = model.count + 1 }
Decrement ->
{ model | count = model.count - 1 }
view : Model -> View Msg
view model =
column [ centerX, spacing 24 ]
[ el [ fontSize 64, bold ]
(text (String.fromInt model.count))
, button [ onTap Increment, glassProminent ]
[ text "+" ]
]

Why Walnut
Messages, models, and views are ordinary typed data. No hidden widget trees, no mystery runtime crashes from mistyped updates. If the compiler accepts it, the app works.
Every app is init / update / view. Side effects are typed commands and subscriptions. There is one way to structure an app and it scales from a counter to a multi-tab production app.
Need HealthKit, Core ML, or a custom UIView? Ports and native "ClassName" open the entire iOS SDK without stuffing it into the language core.
Tooling
Walnut's toolchain is built for tight iteration loops. Typecheck in milliseconds, run tests on your Mac without a Simulator, and hot-reload UI changes into a running app.
case on messages: miss a branch and the compiler rejects itwalnut check in milliseconds, no Xcode build neededwalnut test runs pure update logic on the host JITwalnut simulator --watch hot-reloads without killing the app-- Root update: compile-time exhaustive
update msg model =
dispatch
[ Auth.update
, Nav.update
, Tasks.update
, Persist.update
]
msg
model-- Ports: unbounded native, typed at the edge
Port.cmd "mlx.rank" payload GotRanks
Port.sub "appIntent" IntentFiredOn device
Lumen, Todo, Surfaces, and NativeShell. All compiled from .walnut sources through LLVM.




Language
column [ spacing 12, padding 20 ]
[ el [ bold ] (text model.title)
, button [ onTap Save, glass ]
[ text "Save" ]
]Http.get
{ url = "https://api.example.com/v1"
, expect = Http.expectString GotBody
}uiSwitch
[ propBool "on" model.lit
, onNativeBool "valueChanged" SetLit
]Toolchain
Prebuilt CLI, stdlib, iOS runtime, and docs. Apple Silicon.
brew tap franckverrot/walnut
brew install walnut
walnut version
walnut new HelloApp