Sky: an Elm-inspired language that compiles to typed Go
Sky, an Elm-inspired language that compiles to Go. Hindley-Milner types, server-driven UI (Sky.Live), single binary output.
At a glance
- What is it?
- Sky is a fullstack functional language with Hindley-Milner types that compiles to Go and ships one binary. It is worth a look if you want Elm's discipline on the server, and it is still pre-1.0.
- Who is it for?
- Adopt Sky if your team already writes Go, wants Elm-style types on the server, and can absorb a language whose compiler internals still move between minor versions; the README states public APIs are stable for the v1.0 line, so build on the documented surface, not the compiler crates. Do not adopt it if you need a language with a long release history, a large third-party package index, or a guarantee that a program compiling today still compiles after a minor bump.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Sky is for, and who it is not for
Sky targets a specific kind of team: one that already ships Go services and wants the safety of a typed functional front end without leaving the Go toolchain. The README frames the pitch as "fullstack functional language that compiles to typed Go", with explicit types, exhaustive pattern matching and no runtime exceptions. The output is a single static Go binary, so deployment stays whatever your Go deployment already is.
The design assumption is that the awkward part of a Go web app is not the HTTP layer but the state transitions. Sky takes the Elm architecture as the only application shape: every app is init, update, view and subscriptions, and the same source can run server-side through Sky.Live, in a terminal through Sky.Tui, in a native window through Sky.Webview, or in the browser as wasm through Sky.Spa. If your application does not fit a message-and-model loop, the language is a poor fit regardless of how good the type system is. Scripts, data pipelines and anything that is mostly I/O orchestration will fight the framework rather than benefit from it.
The compile-to-Go mechanism and the Rust compiler rewrite
The pipeline is a Rust compiler that emits Go source, which the Go toolchain then builds. The README states the compiler lives in a cargo workspace under rust/, split into single-responsibility crates: lexer/parser, name resolution, HM inference, type-directed lowering, Go codegen, FFI, formatter and LSP. A query-DAG core is described as the basis for incremental rebuilds and cross-module analysis.
The error model is the part that matters for day-to-day work. Every side effect returns Task Error a and every fallible value returns Result Error a. The README says sky check invokes go build on the emitted Go, so a shape mismatch between Sky's view of a type and what Go actually accepts surfaces at type-check time rather than at runtime. That is a stronger claim than most transpilers make, and it is also the claim most likely to break in edge cases, because it depends on the generated Go being faithfully typed.
The repository still carries legacy-haskell-compiler/ alongside legacy-sky-compiler/ and legacy-ts-compiler/. The README calls the Haskell compiler a "byte-for-byte differential oracle" kept until v1 is tagged. That is a real signal about maturity: the project maintains a second implementation to compare against, which is unusual discipline, but it also means the current compiler has a shorter track record than the language's version number suggests.
Installing Sky and running a first program
The README gives a single-binary install for macOS and Linux, fetched from the main branch of the repository. End users need the sky binary on PATH and Go 1.21 or newer available, because sky build emits Go.
curl -fsSL https://raw.githubusercontent.com/anzellai/sky/main/install.sh | shIf you want to build the compiler rather than download it, the README points at the Rust workspace. A Rust toolchain is required for this path only; the prebuilt binary does not need one.
git clone https://github.com/anzellai/sky
cd sky/rust && cargo build --release -p skyThe README also notes an alternative that installs straight to ~/.local/bin:
cargo install --path rust/crates/sky --root ~/.local --lockedOnce the binary is on PATH, the README's quickstart is two commands. sky init creates a project directory and sky run executes a source file.
sky init hello && cd hello && sky run src/Main.skyThe counter app and what the server prints
The README's counter example is a Sky.Live app. It defines a Msg type with Increment and Decrement, a Model alias with a count field, and init, update and view functions. The update function pattern matches on the message and returns a new model; view returns a Ui.Element Msg. Running it starts a server on port 8000.
sky run src/Main.sky # http://localhost:8000The README notes that adding Std.Tui.app cfg ships the same view to a terminal canvas, and Std.Webview.app cfg ships it to a native desktop window. That is the claim worth checking early: if the same view really does paint to a DOM, to ANSI cells and to a webview without branching, the abstraction is doing real work. If you find yourself writing shape-specific code in view, the shared-view promise is thinner than the README implies.
Adding Go packages without hand-written FFI glue
Sky's interoperability story is a command rather than a binding file. The README states that sky add introspects a Go package and generates strict, typed Sky bindings. It cites the Stripe SDK as an example, claiming roughly 76k FFI symbols compile and tree-shake down to a 4k-line main.go. That is a single reported figure from the project, not an independent measurement, and it says nothing about build times on your machine. It does describe the shape of the trade-off: generated bindings mean the surface is as large as the Go package, and tree-shaking is what keeps the output tractable.
sky add github.com/some/packageThe README does not document how generated bindings are versioned or regenerated when the upstream Go package changes its API. That gap matters more than the symbol count. A Go dependency that renames a method will change the generated Sky surface, and nothing in the README says whether sky check catches that before or after you have written code against the old shape.
Sky.Live versus Sky.Spa, and where the loop runs
The README's shape table is the clearest design statement in the document. Sky.Live is for server-driven web apps: zero client build, instant first paint, server-owned state, real-time updates over one SSE connection per session, browser as the target today. Sky.Spa is for cross-platform apps where UI transitions should be local and instant in client wasm, with a stateless API backend and shipping to a webview on desktop, iOS and Android. The same init/update/view source drives both.
The trade-off is direct. Sky.Live keeps state on the server, which simplifies correctness and makes the client thin, but every interaction that needs a round trip carries network latency and the server holds a connection per session. Sky.Spa removes the round trip but pushes state to the client and requires you to build and ship wasm to four targets. The README does not document offline behaviour, reconnection semantics for the SSE stream, or how state is reconciled when a wasm client and a server disagree. Those are the questions to answer before choosing Spa for anything with mutable shared data.
Where Sky is the wrong tool, and what to use instead
Sky is the wrong choice when the team cannot run Go in the build pipeline. The compiler emits Go and sky check invokes go build, so a Go toolchain is a hard dependency for every developer and every CI runner. If your organisation has standardised on a different runtime, Sky adds a second toolchain rather than replacing one.
A concrete alternative in the same problem space is Elm itself, which Sky's syntax and architecture clearly follow. The difference is the output target. Elm compiles to JavaScript and owns its whole runtime, so it is confined to the browser and has no server story. Sky compiles to Go, which is what lets one source run as a server process, a terminal UI, a native webview or wasm. If your application is browser-only and you want a small, settled language with a long release history, Elm is the more conservative pick. If you need the server and the client to share a language and you are already deploying Go binaries, Sky is the one making that specific trade.
A second limitation is ecosystem shape. Sky's stdlib covers auth, database, HTTP client and server, WebSocket, JSON, JWT, CSV, email, encryption and observability, and the README lists Std.Db, Std.Auth, Std.Ui, Std.Cache and Std.Email by name. That is a broad first-party surface, and it is also entirely first-party. There is no described third-party package index for Sky libraries; the extension path is importing Go packages through sky add. That inverts the usual ecosystem question. You are not choosing between Sky libraries, you are choosing between Go libraries with generated bindings.
Maintenance, versioning and licence cost
The last push to the repository was on 2026-08-25, the same day v0.22.1 was released, with v0.22.0 the day before and v0.21.1 five days before that. The project is not archived and releases are close together. The README describes the current line as v0.23.x and states that public APIs are stable for the v1.0 line while internals can still change between minor versions. Read that literally: the documented public surface is the part you can depend on, and the compiler crates are not.
The repository carries VERSIONING.md, CHANGELOG.md, ROADMAP.md and known-divergences.toml. The last of those is the one to open first, because a file named for known divergences is where a compiler that emits another language records the places where its output does not match its promises. The README does not describe a deprecation policy for stdlib modules, and it does not document rollback or downgrade steps if a minor version breaks a build.
The licence is Apache-2.0, which permits commercial and closed-source use and includes an explicit patent grant. The repository also carries a NOTICE.md, and Apache-2.0 obligations around attribution and notice retention apply to redistribution. That is a description of the licence text, not legal advice; if you redistribute the compiler or embed it in a product, have counsel read NOTICE.md and the licence together. Because the compiler is written in Rust and ships as a binary, the licence you are accepting covers the compiler and the embedded stdlib and runtime, not the Go packages you import through sky add, which carry their own terms.
Editorial conclusion
Adopt Sky if your team already writes Go, wants Elm-style types on the server, and can absorb a language whose compiler internals still move between minor versions; the README states public APIs are stable for the v1.0 line, so build on the documented surface, not the compiler crates. Do not adopt it if you need a language with a long release history, a large third-party package index, or a guarantee that a program compiling today still compiles after a minor bump. Before committing, run sky check on a small real module, confirm the Go toolchain on your build machine is 1.21 or newer, and read VERSIONING.md and known-divergences.toml in the repository, since those two files tell you what the project itself considers unstable.
Frequently asked questions
Does Sky need the Go toolchain installed?
Yes. Sky compiles to Go, and the README states that end users need the sky binary on PATH and Go 1.21 or newer available for codegen. sky check invokes go build on the emitted Go.
What is the difference between Sky.Live and Sky.Spa?
Both run the same init, update, view and subscriptions source. Sky.Live renders server-side with state owned by the server and updates over one SSE connection per session, while Sky.Spa runs the UI loop client-side in wasm and can ship to web, desktop, iOS and Android.
Is Sky's compiler written in Rust or Haskell?
The current compiler is a Rust cargo workspace under rust/. The earlier Haskell compiler was retired and stays in legacy-haskell-compiler/ as a differential oracle until v1 is tagged, according to the README.
Can Sky call existing Go packages?
The README says sky add introspects a Go package and generates strict, typed Sky bindings, so no hand-written FFI glue is needed. It gives the Stripe SDK as an example of a large Go package being bound this way.
Is Sky stable enough for production?
The README states that public APIs are stable for the v1.0 line and that minor versions ship features additively, but it also says internals can still change between minor versions. Treat the documented public surface as the stable part.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/anzellai-sky)