Releases: am-kantox/cure-lang
Cure v0.17.0 -- Proofs & Polish: Toward Idris
Cure v0.17.0 -- Proofs & Polish
Toward Idris.
Cure is a dependently-typed programming language for the BEAM virtual
machine with first-class finite state machines and SMT-backed
verification.
v0.17.0 is the release where the type system grows up. Sigma types,
Pi types, propositional equality, implicit arguments, type-level
reduction, hole-driven development, and totality checking all land
together -- with a matching round of day-to-day quality-of-life
improvements and the first real regression CI.
Highlights
- Dependent-type core:
Sigma,Pi,Eq,refl,rewrite,
implicit{T}arguments, type-levelReduce,?name/??
holes,Cure.Types.Totality, path-sensitive refinement. - Friction-free UX: proper REPL (
:t,:doc,:effects,
:load,:reload,:use,:holes,:env,:reset,:fmt,
:help,:quit, multi-line input, history),cure watch, LSP
inlay hints + signature help + formatting + rename + code lenses +
semantic tokens + workspace symbols,cure newproject templates,
Cure.lock, git dependencies,cure bench,cure test --filter
and--doctests, doctest harvester, ten-chapter tutorial. - Ecosystem groundwork: MIT license,
mix.exsHex package
metadata, VS Code extension scaffold, package-registry design
document. - Quality engineering:
mix cure.checkruns the stdlib and
example regression suites; CI gates every merge on it.
Dependent-type maturation
Sigma types
type Sized(T) = Sigma(n: Nat, Vector(T, n))
A Sigma type pairs a value with a type that depends on it. Recognised
by Type.resolve/1, subtyped componentwise, and convertible to and
from plain tuples.
Pi types
fn append(xs: Vector(T, m), ys: Vector(T, n)) -> Vector(T, m + n)
The return type may reference parameter names. At each call site the
checker substitutes the actual arguments, normalises the result with
Cure.Types.Reduce, and resolves it back to a concrete type. No SMT
call for trivial arithmetic cases.
Propositional equality
refl(x) : Eq(T, x, x)
rewrite eq in expr
Eq(T, a, b) is the type of proofs that a == b. refl(x) is the
only constructor, rewrite the only eliminator. All erased at
runtime via the :cure_refl atom. The new Std.Equal stdlib module
exposes refl, sym, trans, cong.
Implicit arguments
fn id({T}, x: T) -> T = x
{T} introduces an implicit parameter. Cure.Types.Unify solves it
at every call site via first-order unification with an occurs check.
Failures emit a structured :unification_trace pipeline event the
LSP renders in hover and the CLI prints in errors. Implicit
parameters are erased at codegen: they cost nothing at runtime.
Hole-driven development
fn safe_head(xs: List(T)) -> T = ?body
A ?name or ?? placeholder triggers a :hole_goal event carrying
the goal type and the local context at the hole position.
Anonymous ?? holes are numbered ?_1, ?_2, ... in source order.
The REPL's new :holes command lists every hole recorded during the
last evaluation.
Totality
#[total]
fn factorial(n: Int) -> Int
| 0 -> 1
| n -> n * factorial(n - 1)
Cure.Types.Totality classifies every function as :total,
:partial, or :unknown, combining coverage (via
Cure.Types.PatternChecker) with a structural-recursion check. The
default is report-only; #[total] upgrades the classification to a
hard requirement. Mutual recursion arrives in v0.18.0.
Path-sensitive refinement
if x != 0 then 100 / x else 0
Inside the then arm the checker refines x to
{x: Int | x != 0}, so the division is safe without an explicit
refinement annotation. See Cure.Types.PathRefinement and the new
Std.Refine stdlib module (NonZero, Positive, Percentage,
Probability, ...).
Friction-free UX
REPL
Complete rewrite. Multi-line input (terminate with blank line or
;;), meta-commands :t, :doc, :effects, :load, :reload,
:use, :holes, :env, :reset, :fmt, :help, :quit.
Command history persisted to ~/.cure_history.
Watch mode
cure watch lib/ --action check
Recompiles, type-checks, or runs tests on every save with a 200 ms
debounce. No dependency on :file_system; uses a small polling
fallback so it runs out of the box.
LSP polish
Cure.LSP.Server gains:
- Inlay hints (
textDocument/inlayHint). - Signature help (
textDocument/signatureHelp). - Formatting (
textDocument/formatting) routed through the
round-trip-testedCure.Compiler.Printer. - prepareRename + rename (
textDocument/rename). - Code lenses (
textDocument/codeLens). - Semantic tokens (
textDocument/semanticTokens/full). - Workspace symbols (
workspace/symbol).
Project & tooling
cure new <name> [--lib | --app | --fsm]with three templates.Cure.locklockfile;cure deps update,cure deps tree.Cure.Project.resolve_git_dep/2-- git-based dependencies via
git = "...",ref = "..."inCure.toml.cure benchrunner forbench/*.cure.cure test --filter PATTERN --doctests.
Doctests
Any ## or ### block immediately above a function whose body
contains cure> / => pairs becomes a test case:
###
Adds two integers.
cure> Demo.add(2, 3)
=> 5
###
fn add(a: Int, b: Int) -> Int = a + b
cure test --doctests runs them. See
lib/cure/doc/doctests.ex and examples/doctest_demo.cure.
Error catalog
Ten new codes E011-E020 cover implicit-argument failures, sigma
destructuring, totality failures, unfilled holes, refinement
counterexamples, dependent-type mismatches, equality proof
mismatches, and doctest mismatches. Each has a detailed
explanation available via cure explain Eddd or cure why Eddd.
Tutorial
docs/TUTORIAL.md -- ten progressive chapters from "hello world" to
dependent pairs, Pi types, holes, totality, and FSMs.
docs/DEPENDENT_TYPES.md -- full reference for the new type-system
features.
Ecosystem groundwork
- MIT license --
LICENSEat repo root. - Hex package metadata --
mix.exscarries a fullpackage
block and a docs extras list (CHANGELOG, TUTORIAL,
DEPENDENT_TYPES, ...). - VS Code extension scaffold --
vscode-cure/contains a
TypeScriptlanguage-clientwrapper, a TextMate grammar, language
configuration, and a README. Marketplace publication is deferred
to a later release; for nownpm run compile && F5gets you a
working development host. - Package registry design --
docs/PACKAGE_REGISTRY.mdsketches
the manifest, version-constraint solver, and index service that
will land in v0.18.0+.
Language-layer polish
###...###fenced multi-line docstrings -- a single
:doc_commenttoken with common leading indentation stripped.
The single-line## textform is unchanged and now composes
correctly with multi-clause functions in every position.- Trailing
?on identifiers --even?,is_empty?,
positive?lex and compile as single identifiers.?namehole
prefixes are preserved.!stays reserved for effect annotations
and FSM hard events. - Soft keywords as function names --
fn spawn(...),
fn send(...),fn receive(...)are accepted in definitions, so
Std.Fsmcompiles end-to-end. - @extern module-name resolution -- dotted PascalCase atoms like
Elixir.Cure.FSM.Builtinsnow pass through cleanly. - Zero-warning codegen -- mangled variables preserve the
user-visible_nameshape (_V_namerather thanV__name), so
erl_lintnever warns about deliberately unused params.
Regression CI
mix cure.check.stdlib # 20 Std.* modules; errors and warnings fail
mix cure.check.examples # 24 examples with per-file expected output
mix cure.check # both, sequenced
mix check # mix alias: stdlib + cure.check
test/cure/regression_test.exs wraps both as :regression-tagged
ExUnit tests so mix test surfaces them too. The GitHub Actions
workflow (.github/workflows/ci.yml) runs the whole suite on every
push and every pull request.
Numbers
- ~11 new Elixir modules.
- ~12 new test files, ~155 new tests (830 total, 0 failures).
- 2 new stdlib
.curemodules (Std.Equal,Std.Refine); 20 total. - 7 new examples, one for every major new feature.
- 3 new docs (
DEPENDENT_TYPES.md,TUTORIAL.md,
PACKAGE_REGISTRY.md) plus refreshedLANGUAGE_SPEC.md,
TYPE_SYSTEM.md,STDLIB.md. - 0 credo issues in strict mode.
mix formatclean.
mix compile --warnings-as-errorsclean. mix checkgreen (20 stdlib + 24 examples).
Upgrade notes
v0.17.0 is source-compatible with v0.16.0. Three small nudges if you
are porting existing code:
- The lexer now treats a trailing
?on an identifier as part of
the name.x?y(no space) used to tokenise as three tokens; now
x?is one identifier followed byy. Add whitespace (x ? y)
or parentheses if you need the old behaviour. - The
mix.exsversion bumped to0.17.0. If you depend on Cure
via path, your downstreamCure.lockshould be regenerated with
mix cure.compile_stdlib && mix cure.check. - Module-level docstrings written as
## ...lines betweenmod X
and the first definition now reliably attach to the first
definition inside the block. Previously they would sometimes
silently drop. No change is required -- behaviour is strictly
more correct.
What's next -- v0.18.0 Bring the Furniture
The deferred "new ideas" from the v0.17.0 plan come home:
proofcontainers.assert_typebuiltin.- Records with default field values.
- Finished
@derive(Show, Eq, Ord)on records. - Property-based testing in
Std.Test. Std.Iterlazy iterator protocol.- First half of the package registry -- version-constraint parser,
resolver, deeperCure.locksemantics. - Mutual-recursion totality.
Acknowledgements
Thanks to the Finitomata project for the FSM inspiration that landed
in v0.16.0 and continues to pa...