feat: add Ref cells for mutable state (Ref.new, Ref.get, Ref.set, Ref.update)
Implements WISH-013 mutable state primitives. Ref<T> is a mutable container using existing module call syntax. Supported across interpreter, JS, and C backends. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
40
src/types.rs
40
src/types.rs
@@ -49,6 +49,8 @@ pub enum Type {
|
||||
Option(Box<Type>),
|
||||
/// Map type (sugar for App(Map, [K, V]))
|
||||
Map(Box<Type>, Box<Type>),
|
||||
/// Ref type — mutable reference cell holding a value of type T
|
||||
Ref(Box<Type>),
|
||||
/// Versioned type (e.g., User @v2)
|
||||
Versioned {
|
||||
base: Box<Type>,
|
||||
@@ -120,7 +122,7 @@ impl Type {
|
||||
}
|
||||
Type::Tuple(elements) => elements.iter().any(|e| e.contains_var(var)),
|
||||
Type::Record(fields) => fields.iter().any(|(_, t)| t.contains_var(var)),
|
||||
Type::List(inner) | Type::Option(inner) => inner.contains_var(var),
|
||||
Type::List(inner) | Type::Option(inner) | Type::Ref(inner) => inner.contains_var(var),
|
||||
Type::Map(k, v) => k.contains_var(var) || v.contains_var(var),
|
||||
Type::Versioned { base, .. } => base.contains_var(var),
|
||||
_ => false,
|
||||
@@ -161,6 +163,7 @@ impl Type {
|
||||
),
|
||||
Type::List(inner) => Type::List(Box::new(inner.apply(subst))),
|
||||
Type::Option(inner) => Type::Option(Box::new(inner.apply(subst))),
|
||||
Type::Ref(inner) => Type::Ref(Box::new(inner.apply(subst))),
|
||||
Type::Map(k, v) => Type::Map(Box::new(k.apply(subst)), Box::new(v.apply(subst))),
|
||||
Type::Versioned { base, version } => Type::Versioned {
|
||||
base: Box::new(base.apply(subst)),
|
||||
@@ -211,7 +214,7 @@ impl Type {
|
||||
}
|
||||
vars
|
||||
}
|
||||
Type::List(inner) | Type::Option(inner) => inner.free_vars(),
|
||||
Type::List(inner) | Type::Option(inner) | Type::Ref(inner) => inner.free_vars(),
|
||||
Type::Map(k, v) => {
|
||||
let mut vars = k.free_vars();
|
||||
vars.extend(v.free_vars());
|
||||
@@ -288,6 +291,7 @@ impl fmt::Display for Type {
|
||||
}
|
||||
Type::List(inner) => write!(f, "List<{}>", inner),
|
||||
Type::Option(inner) => write!(f, "Option<{}>", inner),
|
||||
Type::Ref(inner) => write!(f, "Ref<{}>", inner),
|
||||
Type::Map(k, v) => write!(f, "Map<{}, {}>", k, v),
|
||||
Type::Versioned { base, version } => {
|
||||
write!(f, "{} {}", base, version)
|
||||
@@ -1946,6 +1950,32 @@ impl TypeEnv {
|
||||
]);
|
||||
env.bind("Map", TypeScheme::mono(map_module_type));
|
||||
|
||||
// Ref module
|
||||
let ref_inner = || Type::var();
|
||||
let ref_type = || Type::Ref(Box::new(Type::var()));
|
||||
let ref_module_type = Type::Record(vec![
|
||||
(
|
||||
"new".to_string(),
|
||||
Type::function(vec![ref_inner()], ref_type()),
|
||||
),
|
||||
(
|
||||
"get".to_string(),
|
||||
Type::function(vec![ref_type()], ref_inner()),
|
||||
),
|
||||
(
|
||||
"set".to_string(),
|
||||
Type::function(vec![ref_type(), ref_inner()], Type::Unit),
|
||||
),
|
||||
(
|
||||
"update".to_string(),
|
||||
Type::function(
|
||||
vec![ref_type(), Type::function(vec![ref_inner()], ref_inner())],
|
||||
Type::Unit,
|
||||
),
|
||||
),
|
||||
]);
|
||||
env.bind("Ref", TypeScheme::mono(ref_module_type));
|
||||
|
||||
// Result module
|
||||
let result_type = Type::App {
|
||||
constructor: Box::new(Type::Named("Result".to_string())),
|
||||
@@ -2185,6 +2215,9 @@ impl TypeEnv {
|
||||
Type::Map(k, v) => {
|
||||
Type::Map(Box::new(self.expand_type_alias(k)), Box::new(self.expand_type_alias(v)))
|
||||
}
|
||||
Type::Ref(inner) => {
|
||||
Type::Ref(Box::new(self.expand_type_alias(inner)))
|
||||
}
|
||||
Type::Versioned { base, version } => {
|
||||
Type::Versioned {
|
||||
base: Box::new(self.expand_type_alias(base)),
|
||||
@@ -2345,6 +2378,9 @@ pub fn unify(t1: &Type, t2: &Type) -> Result<Substitution, String> {
|
||||
// Option
|
||||
(Type::Option(a), Type::Option(b)) => unify(a, b),
|
||||
|
||||
// Ref
|
||||
(Type::Ref(a), Type::Ref(b)) => unify(a, b),
|
||||
|
||||
// Map
|
||||
(Type::Map(k1, v1), Type::Map(k2, v2)) => {
|
||||
let s1 = unify(k1, k2)?;
|
||||
|
||||
Reference in New Issue
Block a user