Sky Lang is a typed functional language designed for AI-era development. HM types, no nulls, no exceptions — compiled to a single Go binary. One codebase targets web, terminal, CLI, and desktop.
module Main exposing (main)
import Std.App as App
import Std.Ui as Ui exposing (Element)
import Std.Ui.Font as Font
type alias Model = { count : Int }
type Msg = Increment | Decrement | Reset
init _ = ({ count = 0 }, Cmd.none)
update msg model =
case msg of
Increment -> ({ model | count = model.count + 1 }, Cmd.none)
Decrement -> ({ model | count = model.count - 1 }, Cmd.none)
Reset -> ({ model | count = 0 }, Cmd.none)
view : Model -> Element Msg
view model =
Ui.column [ Ui.spacing 16, Ui.padding 24 ]
[ Ui.el [ Font.size 48, Font.bold ]
(Ui.text (String.fromInt model.count))
, Ui.row [ Ui.spacing 8 ]
[ Ui.button [] { onPress = Just Decrement, label = Ui.text "-" }
, Ui.button [] { onPress = Just Reset, label = Ui.text "reset" }
, Ui.button [] { onPress = Just Increment, label = Ui.text "+" }
]
]
app =
App.app
{ init = init, update = update
, view = view, subscriptions = \_ -> Sub.none
}
|> App.withNotFound ()
-- web is the default target; sky build --target terminal:tui,
-- terminal:cli, desktop or web:app retargets this same app.
main = App.run appOne App.app — the same init / update / view — runs on five runtimes. Pick with a build-time --target, no view-code changes:
Sky.Live — server-driven web UI with DOM diffing over SSE, persistent sessions, async Cmd.perform
Sky.Spa — the same loop compiled to wasm on the client — web, desktop, iOS, Android from one source (experimental)
Sky.Tui — same Element tree rendered to ANSI cells in a terminal (logical-pixel canvas, mouse + scroll)
Sky.Cli — line-oriented stdin event loop for non-interactive shells
Sky.Webview — native desktop window on the system webview, sharing Sky.Live's renderer (macOS today)
Std.Ui is a typed, no-CSS layout DSL. Pass one build-time --target to retarget — web, terminal, CLI, desktop — with zero changes to view, update, or model. And with Sky.Spa (--target web:app) the same loop compiles to wasm on the client, shipping to web, desktop, iOS and Android from one source.
import Std.App as App
app =
App.app
{ init = init, update = update
, view = view, subscriptions = subs
}
|> App.withNotFound ()
main = App.run app
$ sky build -- web (default)HTTP + SSE wire. Persistent sessions across deploys (memory / SQLite / Redis / Postgres / Firestore). Async Cmd.perform goroutines. Input-authority protocol preserves typed input across re-renders.
-- the very same `app` — no new entry point,
-- no Std.Spa import. Just retarget:
main = App.run app
$ sky build --target web:app
-- desktop:mac | mobile:ios | mobile:androidThe same TEA loop compiled to GOOS=js GOARCH=wasm — runs on the client. `sky run` auto-splits one project into a wasm frontend + a stateless backend + a shared codec (no hand-written boundary). Ships to web, desktop, iOS and Android; Std.Native exposes device APIs (clipboard, camera, geolocation) and Native.bridge extends them. Experimental.
-- the very same `app`, retargeted —
-- the Element tree renders to ANSI cells:
main = App.run app
$ sky build --target terminal:tuiRender Element trees to ANSI cells. Logical-pixel canvas (1280×720 default), mouse left-press + scroll wheel, wide chars + emoji via grapheme-cluster width. Diff renderer; no flicker.
-- the very same `app`, retargeted —
-- a line-oriented loop for non-TTY shells:
main = App.run app
$ sky build --target terminal:cliLine-oriented event loop for non-TTY shells. Same TEA shape; view returns a string each turn. Echo-suppressed password reads via Cli.readPassword for auth flows.
-- the very same `app`, retargeted —
-- Sky.Live's renderer in a native window:
main = App.run app
$ sky build --target desktopNative desktop window on the system webview, reusing Sky.Live's renderer and VNode diff so the same Std.Ui tree renders identically. macOS is smoke-validated today; Windows and Linux build but are untested. Requires cgo.
The 64-spa-native example — clipboard, files, camera and geolocation via Std.Native, plus Native.bridge to add your own — built once and running on web, iOS and Android. Experimental.



Hindley-Milner type inference, exhaustive pattern matching, no null, no exceptions. Undefined names caught at compile time with line:col positions.
Compiles to a native Go binary. Your fullstack app — API, database, server-rendered UI — ships as one file. The compiler itself is also a single binary (Rust).
Std.Ui is a typed no-CSS layout DSL. The same view renders to a server-driven web app (Sky.Live), a client-side wasm app (Sky.Spa), a terminal UI (Sky.Tui), a stdin loop (Sky.Cli), or a desktop window (Sky.Webview). One model, five runtimes.
Import any Go package with auto-generated type-safe bindings. Stripe, Firebase, SQLite, PostgreSQL — all with panic recovery and nil safety.
The compiler propagates HM types into lambda bodies, record fields, list elements, and call args — so callback fields keep their typed callee param (no func(any) any fallback). Parametric records compile to Go generics.
Built-in database (Std.Db), authentication (Std.Auth), JSON encode/decode with pipeline, HTTP server, formatter, LSP with hover + diagnostics.
-- Typed onSubmit decodes formData
-- straight into the Msg's record arg
Ui.form [ Ui.onSubmit DoSignIn ]
[ Ui.input [ Ui.name "username" ]
, Ui.input [ Ui.name "password"
, Ui.htmlAttribute "type" "password" ]
, Ui.input [ Ui.htmlAttribute "type" "submit" ]
]describe shape =
case shape of
Circle r ->
"circle r=" ++ String.fromFloat r
Rectangle w h ->
"rect" import Std.Db as Db
-- reads sky.toml [database]
todos =
Db.connect ()
|> Task.andThen (\db ->
Db.queryDecode db
"SELECT * FROM todos" [] todoDecoder)-- Run tasks in background goroutines
update msg model =
case msg of
FetchData ->
( model, Cmd.perform
(Http.get "/api") GotData )-- Parallel HTTP requests (goroutines)
fetch =
Task.parallel
[ Http.get url1
, Http.get url2
]
|> Task.andThen handleAllimport Github.Com.Stripe.StripeGo.V84 as Stripe
-- whole-program DCE keeps only the
-- symbols you call (76k available)
customer email =
Stripe.newCustomerParams ()
|> Result.andThen
(\p -> Stripe.customerParamsSetEmail email p)Standalone tools, libraries, and full applications.
E-commerce app with Stripe payments, Firebase auth, Firestore, i18n, Tailwind CSS. Full Sky.Live TEA architecture with async commands.
Chess game with negamax AI, SQLite persistence, and SSE real-time updates. Full move validation, castling, en passant, promotion.
Encrypted environment variable manager. AES-256-CBC SQLite, portable across machines, six commands. Single 8MB binary.
Tailwind CSS utility classes for Sky.Live apps. Type-safe, composable, zero-runtime — class names checked at compile time.
Roadmap voting app — Sky.Live, Std.Auth (bcrypt + sessions), Std.Db (SQLite), email verification, key rotation.
Service monitoring dashboard with metrics, alerts, and incident timeline. Real-time updates via SSE subscriptions.
Reddit/HN-style forum on the typed no-CSS layout DSL. Per-user vote tracking, threaded comments, password-manager-friendly forms.
Install Sky. Bootstrap your first project. Ship a single binary.
curl -fsSL https://raw.githubusercontent.com/anzellai/sky/main/install.sh | sh
sky init my-app
cd my-app
sky run