Types
Status: the Rust compiler (
rust/,cargo build --release -p sky) is the primary Sky compiler; the Haskell compiler is preserved underlegacy-haskell-compiler/. Verified by the example sweep + compiler test suite (cargo test+ xtask gates). See../compiler/versions.mdfor the changelog.
Sky's type system is Hindley-Milner with algebraic data types, records, and concrete Go interop types. There are no type classes, no higher-kinded types, no row polymorphism.
Primitives
| Sky | Go |
|---|---|
Int | int |
Float | float64 |
String | string |
Bool | bool |
Char | rune (int32) |
Bytes | []byte |
Type aliases
type alias Point =
{ x : Int
, y : Int
}
type alias UserId = String
type alias Tags = List String
Every record type alias auto-generates a positional constructor:
origin : Point
origin =
Point 0 0 -- constructor args in field-declaration order
Algebraic data types
type Shape
= Circle Float
| Rect Float Float
| Polygon (List Point)
area : Shape -> Float
area shape =
case shape of
Circle r ->
3.14159 * r * r
Rect w h ->
w * h
Polygon points ->
-- exhaustiveness-checked at compile time
polygonArea points
Pattern matches are exhaustive — missing variants are build errors.
Tuples
Fixed-arity product types. Arity 2 emits rt.SkyTuple2 ({V0, V1}); arity 3 emits rt.SkyTuple3 ({V0, V1, V2}); arity 4+ falls back to the slice-backed rt.SkyTupleN. The mapping is performed in Sky.Generate.Go.Type.typeToGo and is invisible at the source level — destructuring patterns work the same way at every arity.
pair : ( Int, String )
pair =
( 42, "answer" )
Lists & dicts
numbers : List Int
numbers =
[ 1, 2, 3 ]
usersByEmail : Dict String User
usersByEmail =
Dict.empty
|> Dict.insert "alice@example.com" alice
Dictismap[string]anyat runtime. Non-Stringkeys are stringified. Arithmetic onDict Int vkeys returned byDict.toListsilently produces strings — iterate viaDict.getover known key ranges instead.
Maybe & Result
Maybe a
= Just a
| Nothing
Result e a
= Ok a
| Err e
Use Maybe for optional values, Result for fallible pure computations. Both are generic in their payload type.
Since v0.9, every public fallible surface uses Result Error a (not Result String a). See ../errors/error-system.md.
Task
Task e a is the Sky effect type. Every effectful operation — file I/O, HTTP, DB, println — returns Task Error a. Run one with Task.perform.
readConfig : Task Error String
readConfig =
File.readFile "./config.json"
|> Task.onError (\_ -> Task.succeed "{}")
Type annotations
Annotations are load-bearing:
- If a function is annotated, the annotation is the scheme used by callers. The body is checked against it, not just inferred and cross-referenced.
- Missing annotations fall back to inferred types (full HM, including generalisation).
- Type variables in annotations are distinct:
f : a -> b -> agets fresh TVars foraandb.
Generics
Polymorphic HM-inferred functions lower to Go generics:
identity : a -> a
identity x = x
func Identity[T1 any](x T1) T1 { return x }
solvedTypeToGo TVar falls back to any at expression positions (Go's type parameters can't appear outside enclosing function signatures). This is by design, not an escape hatch.
Parametric record aliases (v0.15+) lower to Go-generic structs with one type parameter per HM type variable:
type alias Cfg msg =
{ onSubmit : msg
, label : String
}
type Cfg_R[T1 any] struct {
OnSubmit T1
Label string
}
Per-instance construction takes the type args: Cfg_R[Msg], Cfg_R[Int]. Callback fields keep their typed callee parameter — no func(any) any fallback at parametric-record slots. The same-module polymorphic re-instantiation rule lets f : Cfg msg -> msg be called with msg=Int AND msg=Bool in the same module without pinning.
Type variables with constraints
Intentionally unsupported. Sky's HM is unconstrained; typeclass-style operations are provided implicitly via runtime helpers:
- Equality — the
==and/=operators dispatch throughrt.sky_equal, which type-switches on the runtime tag and recurses into ADTs / records / lists / dicts. Works for any value without a constraint. - Ordering —
<,>,<=,>=, andBasics.comparedispatch throughrt.sky_comparefor primitives and lexicographic ordering on collections. - Display / debug —
Basics.toString(aliasDebug.toString) renders any value to a readable string for debugging. For production formatting use the typed helpers (String.fromInt,String.fromFloat).
There are no Eq / Ord / Show constraints to opt into — every operator just works on every type.