Heat score
1Topic analysis
Sky – an Elm-inspired language that compiles to Go
Experimental -- Sky is under active development. APIs and internals will change. Sky is an experimental programming language that combines Go's pragmatism with Elm's elegance to create a simple, fullstack language where you write FP code and ship a single portable binary. What Sky brings together: Sky compiles to Go. You get a single binary that runs your fullstack app -- API server, database access, and server-rendered interactive UI -- all from one codebase, one language, one deployment artifact. The compiler, CLI, formatter, and LSP are all self-hosted -- written in Sky itself, compiled to a ~4MB native Go binary. Zero Node.js/TypeScript/npm dependencies. The compiler bootstraps through 3+ generations of self-compilation. I've worked professionally with Go, Elm, TypeScript, Python, Dart, Java, and others for years. Each has strengths, but none gave me everything I wanted: simplicity, strong guarantees, functional programming, fullstack capability, and portability -- all in one language. The pain point that kept coming back: startups and scale-ups building React/TypeScript frontends talking to a separate backend, creating friction at every boundary -- different type systems, duplicated models, complex build pipelines, and the constant uncertainty of "does this actually work?" that comes with the JS ecosystem. Maintenance becomes the real cost, not the initial build. I always wanted to combine Go's tooling (fast builds, single binary, real concurrency, massive ecosystem) with Elm's developer experience (if it compiles, it works; refactoring is fearless; the architecture scales). Then, inspired by Phoenix LiveView, I saw how a server-driven UI could eliminate the frontend/backend split entirely -- one language, one model, one deployment. The first attempt compiled Sky to JavaScript with the React ecosystem as the runtime. It worked, but Sky would have inherited all the problems I was trying to escape -- npm dependency chaos, bundle configuration, and the fundamental uncertainty of a dynamically-typed runtime. So I started over with Go as the compilation target: Elm's syntax and type system on the frontend, Go's ecosystem and binary output on the backend, with auto-generated FFI bindings that let you import any Go package and use it with full type safety. Building a programming language is typically a years-long effort. What made Sky possible in weeks was AI-assisted development -- first with Gemini CLI, then settling on Claude Code, which fits my workflow and let me iterate on the compiler architecture rapidly. I designed the language semantics, the pipeline, the FFI strategy, and the Live architecture; AI tooling helped me execute at a pace that would have been impossible alone. Sky is named for having no limits. It's experimental, opinionated, and built for one developer's ideal workflow -- but if it resonates with yours, I'd love to hear about it. Prerequisite : Go must be installed (Sky compiles to Go). Pre-built images are available on Docker Hub: Every Sky file declares a module with an exposing clause: Module names are PascalCase and hierarchical (dot-separated). The file path mirrors the module name: Utils.String lives at src/Utils/String.sky . Sky.Core.Prelude is implicitly imported into every module (provides Result , Maybe , errorToString , etc.). Sky uses Hindley-Milner type inference with type class constraints. Type annotations are optional but recommended for top-level definitions. The type system enforces correctness at compile time -- if it compiles, it runs. Sky enforces three built-in type constraints, checked at compile time: Passing the wrong type is a compile error: Constructors can carry zero or more typed fields. The compiler performs exhaustiveness checking on pattern matches. All functions are curried and support partial application. Bindings in let can have optional type annotations. Each binding is in scope for all subsequent bindings and the body. The compiler checks exhaustiveness -- it will warn if you miss a case. Pipelines are the idiomatic way to chain operations: Equivalent to List.head (String.split " " (String.toLower (String.trim " Hello, World! "))) . if is an expression -- both branches must return the same type. See Pattern Matching . Sky can import any Go package. The compiler auto-generates type-safe, Task-wrapped bindings with panic recovery. Users never write FFI code. Principle : all Go interop returns Task String T -- effects are explicit, panics are caught, nil is handled. Every Go call is wrapped with defer recover() . Panics become Err : Go's Package.Method becomes packageMethod in Sky (lowerCamelCase): Go structs are opaque types in Sky. The compiler generates constructors, field getters, and pipeline-friendly setters for each struct: Pointer fields ( *string , *int64 , *bool ) are handled transparently -- pass the plain value, the wrapper creates the pointer. Nested structs are built bottom-up and passed to parent setters. For large Go packages (Stripe SDK: 8,896 types), the sky-ffi-gen native tool generates only bindings for symbols actually referenced in source code, reducing compile time by 100x. Go callbacks are automatically bridged: Sky supports The Elm Architecture for stateful applications: Key modules: Std.Cmd , Std.Sub , Std.Task , Std.Program . Elm-compatible JSON encoding/decoding: Sky wraps all effectful operations in Task . Tasks are lazy -- they only execute when perform is called. Example -- parallel HTTP requests: Example -- sequential vs parallel: Task.parallel preserves result order -- the i-th result corresponds to the i-th task. If any task fails, the first error is returned. Under the hood, each task runs in its own goroutine with panic recovery. List.parallelMap is the pure equivalent for non-Task computations: Full HTML element and attribute support for Sky.Live and server-rendered apps: Elements: div , section , article , aside , header , footer , nav , main , h1 - h6 , p , span , strong , em , a , ul , ol , li , form , label , button , input , textarea , select , option , table , thead , tbody , tr , th , td , img , br , hr , pre , code , blockquote , and more. Std.Css provides typed CSS properties: display , flexDirection , justifyContent , alignItems , padding , margin , color , backgroundColor , fontSize , borderRadius , boxShadow , transition , transform , units ( px , rem , em , pct , vh , vw ), colors ( hex , rgb , rgba , hsl , hsla ), and 100+ more. Sky.Live is a server-driven UI framework inspired by Phoenix LiveView . Write standard TEA code; the compiler generates a Go HTTP server with DOM diffing, session management, SSE subscriptions, and a tiny (~3KB) JS client. No WebSocket required. No client-side framework. Works on Lambda, Cloud Run, any HTTP host. Sky.Live events accept typed Msg constructors -- no string-based events needed: For non-Live server-rendered HTML, use Std.Html.Events which returns (String, String) attribute tuples with JavaScript handlers: Subscriptions let the server push updates to the browser without user interaction. The Sub type is a proper ADT: Time.every 1000 Tick constructs a SubTimer 1000 Tick value. At runtime, the Go server inspects this value, starts a timer goroutine, and pushes DOM patches via Server-Sent Events. Sky.Live components follow the Elm convention: module name = type name. A component exports Foo , Msg , init , update , and view . The compiler auto-wires component messages when the naming convention is followed: See docs/design/sky-live-components.md for the full protocol. For multi-module Sky.Live apps, define Page, Model, and Msg in a shared State.sky module: This avoids circular dependencies and gives all modules access to typed Msg constructors. See examples/12-skyvote for a full example. Sky.Live config values from sky.toml are embedded at compile time, but can be overridden at runtime via environment variables or a .env file. Env var names mirror the sky.toml structure with underscores. Priority (lowest to highest): compiled defaults sky.toml .env file. See the design docs for the full architecture: Sky has a built-in package manager that handles both Sky packages and Go packages. The sky.toml file is the project manifest. Here is a complete reference: A project's role is determined by which fields are present: Both sky add and sky remove automatically update sky.toml — dependencies are added to [dependencies] (Sky packages) or [go.dependencies] (Go packages) and removed from the relevant section. Auto-detection : When you run sky add github.com/... , Sky checks the remote repository: Transitive dependencies : When installing a Sky package, its own dependencies (both Sky and Go) are automatically installed recursively. After sky add github.com/someone/sky-utils (assuming it exposes Utils.String ), three import syntaxes are supported: All three resolve to the same file in .skydeps/ . The resolver respects each package's [lib].exposing list -- only publicly exposed modules are importable. Resolution precedence : local src/ modules > .skydeps/ packages > stdlib. If a local module name conflicts with a dependency, use the full or prefixed import path to reach the dependency. After sky add github.com/google/uuid : To make a Sky package importable by others: Only modules listed in [lib].exposing are importable. Internal modules (helpers, implementation details) remain private. A library can also have Go dependencies. When someone installs your Sky package, its [go.dependencies] are transitively installed as well. If file.sky is omitted, defaults to src/Main.sky . sky check runs the full type-checking pipeline without compiling to Go. It resolves imported ADT constructors, type aliases, and annotations from dependency modules — matching the same environment used during sky build : Multiple errors are reported per file (the parser recovers from syntax errors and continues). sky fmt formats Sky code in Elm style: Sky ships with a Language Server that provides: Sky includes Helix editor integration. Configure in your Helix languages.toml : Community-contributed Zed extension with syntax highlighting and LSP integration: sky-zed by @TheGB0077 The Sky compiler is self-hosted -- written in Sky, compiling to Go, then compiling itself. Building the largest example project (SkyShop: 43 local modules + 14 FFI modules including Stripe SDK, Firebase, Tailwind CSS) exposed a series of performance bottlenecks. Here's how each was identified and fixed. SkyShop's build was hanging indefinitely -- the compiler never completed. The root cause: the loadFfiForTypeCheck function loaded the full Stripe SDK binding file (8.4 MB, 147K lines) once per dependency module. With 43 local modules each triggering a separate FFI loading pass, the compiler was parsing the Stripe SDK ~40 times. Result: Hanging -> 0:59 warm / 1:30 cold (with ~200% CPU utilisation on multi-core machines). The original pipeline loaded FFI bindings per-module: FFI .skyi modules only need constructor declarations and wrapper variable bindings -- not full type-checking or AST-to-Go lowering. The light path generates just what's needed: Sky now has Task.parallel and List.parallelMap -- pure functional interfaces backed by Go goroutines: The compiler uses List.parallelMap for the three most expensive sequential operations: The parallel helpers are written to a separate Go file ( sky-out/sky_parallel.go ) with proper multi-line formatting, avoiding the goimports issue where single-line function bodies cause import stripping. Sky's ++ operator compiles to Go string concatenation, which is O(n) per operation. Chained concatenation a ++ b ++ c ++ d creates O(n^2) intermediate strings. The fix: replace hot-path chains with String.join "" [parts] , which uses Go's strings.Join (single allocation): Applied to the lowerer's emitGoExprInline (called per AST node), lowerBinary (per operator), emitBranchCode (per case branch), and patternToCondition (per pattern match). Dependency modules that haven't changed don't need re-lowering. The compiler caches lowered Go declarations in .skycache/lowered/ : On subsequent builds, cached modules skip type-checking and lowering entirely. Cross-module aliases are regenerated fresh each build to avoid duplicates. The cache is invalidated by sky clean or by deleting .skycache/lowered/ . Large Go packages like Stripe SDK (8,896 types, 7,824 constants) overwhelm the Sky-based binding generator. The native sky-ffi-gen tool (Go binary) solves this by scanning source files for used symbols before generating bindings: The tool extracts the import alias from import ... as Stripe , scans src/ for Stripe.funcName patterns, then filters the inspect.json to only include referenced symbols and their transitive type dependencies. Three Go runtime functions dominated compilation time: sky_equal (Step 9) -- used for every == comparison in Sky. The original implementation converted both values to strings via fmt.Sprintf before comparing. The fix adds a type-switch fast path: SkyADT struct + integer tag matching (Steps 13, v0.7.11–v0.7.14) -- ADT values were map[string]any{"Tag": 0, "SkyName": "Navigate", "V0": page} . Now they're Go structs with integer tag comparison: The SkyADT struct eliminates map allocation per ADT value. Pattern matching is O(1) integer comparison instead of O(n) string hashing. Field access is a direct struct field read instead of map lookup. The current compiler (v0.7.x) uses any for function parameters and returns. ADT values use typed Go structs (SkyADT) with integer tag matching, but function boundaries remain untyped. The v1.0 goal is to eliminate any from all generated code. Already done toward v1.0: Sky's core principle is "if it compiles, it works" . A comprehensive audit identified 33 type safety gaps and all have been addressed. The compiler now self-hosts cleanly with strict type checking -- all 15 examples compile with zero warnings. The parser's parseCaseBranches function used a fixed column check ( peekColumn ) to terminate case branch parsing. Nested case expressions would absorb outer branches as dead code. Fix: parseCaseBranches now tracks branchCol (the column of the first branch) and terminates at peekColumn . Eight compiler source files were refactored to extract nested case expressions into helper functions. Three improvements ensure the type checker works correctly across module boundaries: Imported ADT constructors -- The ADT registry now merges constructors from ALL imported modules, not just the entry module. Pattern matching on imported constructors (e.g., BoardPage from State.sky ) type-checks correctly. Imported type aliases -- Type aliases ( type alias Model = { ... } ) from imported modules are available during annotation checking. { model | field = x } correctly resolves to Model when Model is defined in another module. Record update types -- Record update expressions ( { record | field = value } ) now infer the BASE record's type, not a partial record with only the updated fields. Case expressions that don't cover all constructors previously returned nil silently. Now: Dependency modules are cached in /.skycache/lowered/ with source fingerprints. Caches are project-scoped (not shared between projects) and invalidated when the source changes. The compiler automatically runs go mod tidy when Go dependencies are missing. Sky is under active development. These are current limitations to be aware of: These will be addressed in future versions. The nested case limitation is tracked as the top priority for v0.8. Sky is experimental and under active development. Contributions are welcome! Here's how you can help: If you're interested, open an issue or start a discussion. PRs are welcome for bug fixes, examples, and stdlib additions. MIT License. See LICENSE for details.
Sources
1Platforms
1Relations
4- First seen
- Apr 6, 2026, 11:22 PM
- Last updated
- Apr 7, 2026, 4:00 AM
Why this topic matters
Sky – an Elm-inspired language that compiles to Go is currently shaped by signals from 1 source platforms. This page organizes AI analysis summaries, 1 timeline events, and 4 relationship edges so search engines and AI systems can understand the topic's factual basis and propagation arc.
Keywords
10 tagsSource evidence
1 evidence itemsSky – an Elm-inspired language that compiles to Go
News · 1Timeline
Sky – an Elm-inspired language that compiles to Go
Apr 6, 2026, 11:22 PM