v0.15 state: type-directed lowering throughout, Go generics on parametric record aliases. Layer-3 stdlib, whole-program DCE (Stripe-SDK scale: −82 % source), LSP 100 % coverage; runtime verification across all 27 examples (120 stdlib assertions + 306 cabal specs). See
../compiler/versions.mdfor the changelog.
Since v0.9, every fallible operation in Sky returns a value whose error slot is Sky.Core.Error — a structured ADT with eleven kinds and typed details. There is no more Result String or Task String on any public surface.
module Sky.Core.Error exposing (..)
type Error
= Error ErrorKind ErrorInfo
type ErrorKind
= Io
| Network
| Ffi
| Decode
| Timeout
| NotFound
| PermissionDenied
| InvalidInput
| Conflict
| Unavailable
| Unexpected
type alias ErrorInfo =
{ message : String
, details : ErrorDetails
}
type ErrorDetails
= NoDetails
| FfiPanic String
| TypeMismatch String String
| HttpStatus Int
| JsonDecode String
| Custom (Dict String String)io : String -> Error
network : String -> Error
ffi : String -> Error
decode : String -> Error
timeout : String -> Error
notFound : Error
permissionDenied : Error
invalidInput : String -> Error
conflict : String -> Error
unavailable : String -> Error
unexpected : String -> ErrorPlus builders:
withMessage : String -> Error -> Error
withDetails : ErrorDetails -> Error -> Error
toString : Error -> String
isRetryable : Error -> Boolimport Sky.Core.Error as Error exposing (Error(..), ErrorKind(..))
loadUser : Db -> String -> Task Error (List User)
loadUser db id =
Db.queryDecode db "SELECT * FROM users WHERE id = ?" [ id ] userDecoder
|> Task.mapError classifyError
classifyError : Error -> Error
classifyError e =
case e of
Error PermissionDenied info ->
info.message
|> String.append "user lacks permission: "
|> Error.invalidInput
_ ->
eEvery runtime kernel that can fail returns rt.SkyResult[any, any] where the Err slot is a value produced by one of the Err* builders in runtime-go/rt/rt.go:
ErrIo(msg string) any
ErrNetwork(msg string) any
ErrFfi(msg string) any
ErrDecode(msg string) any
ErrTimeout(msg string) any
ErrNotFound() any
ErrPermissionDenied() any
ErrInvalidInput(msg string) any
ErrConflict(msg string) any
ErrUnavailable(msg string) any
ErrUnexpected(msg string) anyThese produce skyErrorAdt values that match the Sky ADT's runtime layout ({Tag, SkyName, V0 (kind), V1 (info)}), so Sky-side pattern matching works without a translation layer.
func SkyFfiRecover(out *any) func()
func SkyFfiRecoverT[A any](out *SkyResult[any, A]) func()Every FFI wrapper defers one of these. On panic, out is set to Err(ErrFfi(<panic-message>)) with an FfiPanic detail carrying the recovered value. Sky code never sees a Go panic.
| Kind | Use for |
|---|---|
Io |
File / DB / disk failures |
Network |
HTTP / DNS / transport failures |
Ffi |
Panics caught at the FFI boundary |
Decode |
JSON / binary decoder failures |
Timeout |
Deadlines, context cancellation |
NotFound |
Record or resource absent |
PermissionDenied |
Auth / access-control failures |
InvalidInput |
User input validation |
Conflict |
Optimistic-lock / unique-constraint failures |
Unavailable |
Service overloaded / circuit-broken |
Unexpected |
Fallback when no other kind fits |
isRetryable returns True for Timeout, Network, and Unavailable. Use it in retry / backoff policies.
If you're upgrading a pre-v1 project:
-- Before:
loadUser : String -> Task String User
-- After:
loadUser : String -> Task Error UserIf your error branch was Err e -> Err (Error.io e) and the upstream now returns Error (not String), collapse to Err e -> Err e. Error chaining through combinators preserves the kind.
Forbidden in v0.9+ public surfaces (enforced by Sky.ErrorUnificationSpec):
Result String aTask String aStd.IoError(deleted)RemoteData(deleted)