Skip to content

v0.2.0

Choose a tag to compare

@sdiehl sdiehl released this 25 Jun 10:31
· 2 commits to main since this release
c56b8f5

0.2.0

  • Fixed-width bitwise and shift builtins on the I64/U64 lanes: i64_and/i64_or/i64_xor/i64_shl/i64_shr and their u64_* counterparts. and/or/xor share one bit pattern across lanes; i64_shr is arithmetic, u64_shr logical; shift counts are taken modulo 64.
  • system(String) -> Int runs a shell command and returns its exit code, and eprint/eprintln write to stderr, so a program can drive external tools and emit diagnostics off the stdout stream.
  • Superclasses: a class may declare another as a superclass (class Ord(a) given Eq(a)). Each instance carries a resolved superclass dictionary as a leading field of its dict cell, and a given Ord(a) constraint discharges an Eq(a) obligation by projecting it, found automatically from the instances in scope. The prelude's Ord now requires Eq.
  • Growable mutable Array(a) (array_new/array_empty/array_len/array_get/array_set/array_push), an ordinary reference-counted heap cell so drops recurse into its elements. array_set and array_push write in place when the array is uniquely owned (FBIP) and copy otherwise; array_push doubles capacity when full, so appends are amortized O(1). The prelude adds array_of_list.
  • string_of_array and the prelude concat_all/array_of_list build a string from many chunks in a single allocation, replacing the quadratic right-nested concat chain.
  • Prelude HashMap(v): a separate-chaining hash table with String keys built on the growable array (hm_new/hm_insert/hm_lookup/hm_member/hm_get_or/hm_keys/hm_values/hm_size/hm_to_list/hm_delete/hm_from_list/hm_adjust), doubling its bucket count past load factor 1. Keys hash by a fixed-width FNV-1a written in the language, so iteration order is a deterministic function of the inserts.
  • O(1) byte access: byte_at/byte_len (UTF-8 unaware) and string_of_bytes, so a lexer or hash scans raw bytes in linear time. array_pop rounds out the array API, and array_foldl/array_to_list are added to the prelude.
  • Surface fixed-width arithmetic: i64_*/u64_* add/sub/mul/div/rem/cmp (wrapping, no bignum promotion), enabling a real fixed-width hash in userland.
  • Higher-kinded types: a class parameter may range over a type constructor (kind * -> *), applied as f(a) in method signatures, with instance resolution keyed on the head constructor. The prelude adds the Functor/Applicative/Monad/Foldable/Traversable tower with List and Option instances. fmap/traverse are effect-polymorphic, so the per-element effect row threads through instead of an Applicative wrapper (effects, not do-notation).
  • String-utility prelude: character classifiers (is_digit/is_alpha/is_alnum/is_space/is_upper/is_lower, to_upper_c/to_lower_c), starts_with/ends_with/contains/index_of, to_upper/to_lower/trim, and args().
  • prism fmt separates top-level declarations with a blank line.
  • Editor tooling: a dependency-free Neovim highlighter under scripts/nvim/ (an ftdetect/ filetype map for *.pr plus a syntax/ highlighter), with its keyword set mirrored from src/lex/token.rs so it tracks the lexer.
  • A project and module system. A prism.toml manifest ([package] name, [bin] entry) plus a src/ tree make a multi-file project that prism run/build compiles, resolving module paths from the project root rather than the entry file's directory (single-file invocation is unchanged). Name resolution now canonicalizes every top-level definition to a module-qualified symbol (Data.Map.insert for exports, Data.Map@helper for privates), so qualified references (M.x) reach disjoint namespaces and two modules may export the same short name and coexist; selective imports (import M (a, b)) bring only the listed names into bare scope, and pub controls what an importer can reach. Instances record their defining module: an orphan instance (defined apart from both its class and its type) and instances overlapping across modules are reported as warnings, and an ambiguity names each candidate's module.
  • Module-system follow-ups: pub import M (x) re-exports the named items through the importing module, with chains resolving transitively to the original definition; a module's full dotted path qualifies a reference (Geo.Util.one), not only its last component (Util.one); and orphan/overlapping-instance warnings render with a source caret when they point into the program being compiled.
  • Static fip/fbip checking of the FP^2 discipline. fbip proves zero fresh allocation and that an annotated body calls only annotated, allocation-free functions; fip additionally proves linearity (each owned non-immediate binding consumed at most once, checked on the source term with scalars exempt) and bounded stack (every recursive call in the call-graph SCC is a tail call or a single tail-modulo-cons/-add). The tail/TRMC classification is shared with codegen (src/core/tailrec.rs), so an accepted fip function always lowers to a loop; fip may call only fip, fbip may call either. Recursive accumulator/TRMC functions like rev_onto and bump now type-check as fip and run in constant heap and stack.
  • A cyclic superclass hierarchy (class A given B, class B given A) is now reported as a superclass cycle: A -> B -> A error at class-construction time, instead of overflowing the stack the first time the chain is walked at a use site.
  • Pattern coverage now treats an irrefutable if true guard as always-matching: such an arm completes exhaustiveness like an unguarded one (no spurious non-exhaustive error) and shadows any later arm (which is reported unreachable). Fallible guards keep their conservative non-covering treatment.
  • The fip bounded-stack rejection now explains when a function joins the tail-recursion group only because another function flows as a first-class value (a capture) rather than through a direct call cycle, pointing at the fix (call directly or annotate fbip). The set of accepted programs is unchanged; only the diagnostic is sharper.
  • Rank-N polymorphism is predicative: a forall written directly as a type-constructor argument (List(forall a. (a) -> a)) is now rejected at the annotation with a clear "impredicative type" message naming the constructor, instead of surfacing later as a confusing expected a, got Int mismatch from a leaked rigid variable. Higher-rank types remain available as function parameters, results, and declared data fields (a polymorphic field carries a forall through a generic container).