fix: C backend String functions, record type aliases, docs cleanup
- Add String.fromChar, chars, substring, toUpper, toLower, replace, startsWith, endsWith, join to C backend - Fix record type alias unification by adding expand_type_alias and unify_with_env functions - Update docs to reflect current implementation status - Clean up outdated roadmap items and fix inconsistencies - Add comprehensive language comparison document Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
126
src/types.rs
126
src/types.rs
@@ -1173,6 +1173,73 @@ impl TypeEnv {
|
||||
},
|
||||
);
|
||||
|
||||
// Add Sql effect for database access
|
||||
// Connection is represented as Int (connection ID)
|
||||
let row_type = Type::Record(vec![]); // Dynamic record type
|
||||
env.effects.insert(
|
||||
"Sql".to_string(),
|
||||
EffectDef {
|
||||
name: "Sql".to_string(),
|
||||
type_params: Vec::new(),
|
||||
operations: vec![
|
||||
EffectOpDef {
|
||||
name: "open".to_string(),
|
||||
params: vec![("path".to_string(), Type::String)],
|
||||
return_type: Type::Int, // Connection ID
|
||||
},
|
||||
EffectOpDef {
|
||||
name: "openMemory".to_string(),
|
||||
params: vec![],
|
||||
return_type: Type::Int, // Connection ID
|
||||
},
|
||||
EffectOpDef {
|
||||
name: "close".to_string(),
|
||||
params: vec![("conn".to_string(), Type::Int)],
|
||||
return_type: Type::Unit,
|
||||
},
|
||||
EffectOpDef {
|
||||
name: "execute".to_string(),
|
||||
params: vec![
|
||||
("conn".to_string(), Type::Int),
|
||||
("sql".to_string(), Type::String),
|
||||
],
|
||||
return_type: Type::Int, // Rows affected
|
||||
},
|
||||
EffectOpDef {
|
||||
name: "query".to_string(),
|
||||
params: vec![
|
||||
("conn".to_string(), Type::Int),
|
||||
("sql".to_string(), Type::String),
|
||||
],
|
||||
return_type: Type::List(Box::new(Type::var())), // List of records
|
||||
},
|
||||
EffectOpDef {
|
||||
name: "queryOne".to_string(),
|
||||
params: vec![
|
||||
("conn".to_string(), Type::Int),
|
||||
("sql".to_string(), Type::String),
|
||||
],
|
||||
return_type: Type::Option(Box::new(Type::var())), // Optional record
|
||||
},
|
||||
EffectOpDef {
|
||||
name: "beginTx".to_string(),
|
||||
params: vec![("conn".to_string(), Type::Int)],
|
||||
return_type: Type::Unit,
|
||||
},
|
||||
EffectOpDef {
|
||||
name: "commit".to_string(),
|
||||
params: vec![("conn".to_string(), Type::Int)],
|
||||
return_type: Type::Unit,
|
||||
},
|
||||
EffectOpDef {
|
||||
name: "rollback".to_string(),
|
||||
params: vec![("conn".to_string(), Type::Int)],
|
||||
return_type: Type::Unit,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
// Add Some and Ok, Err constructors
|
||||
// Some : fn(a) -> Option<a>
|
||||
let a = Type::var();
|
||||
@@ -1743,6 +1810,65 @@ impl TypeEnv {
|
||||
|
||||
TypeScheme::poly(type_vars, typ.clone())
|
||||
}
|
||||
|
||||
/// Expand a Named type to its underlying structural type if it's an alias
|
||||
/// This is needed for unifying record type aliases with record literals
|
||||
pub fn expand_type_alias(&self, ty: &Type) -> Type {
|
||||
match ty {
|
||||
Type::Named(name) => {
|
||||
if let Some(type_def) = self.types.get(name) {
|
||||
match type_def {
|
||||
TypeDef::Alias(inner) => self.expand_type_alias(inner),
|
||||
// For enums and records, keep the Named type
|
||||
_ => ty.clone(),
|
||||
}
|
||||
} else {
|
||||
ty.clone()
|
||||
}
|
||||
}
|
||||
Type::Function { params, return_type, effects, properties } => {
|
||||
Type::Function {
|
||||
params: params.iter().map(|p| self.expand_type_alias(p)).collect(),
|
||||
return_type: Box::new(self.expand_type_alias(return_type)),
|
||||
effects: effects.clone(),
|
||||
properties: properties.clone(),
|
||||
}
|
||||
}
|
||||
Type::App { constructor, args } => {
|
||||
Type::App {
|
||||
constructor: Box::new(self.expand_type_alias(constructor)),
|
||||
args: args.iter().map(|a| self.expand_type_alias(a)).collect(),
|
||||
}
|
||||
}
|
||||
Type::Tuple(elems) => {
|
||||
Type::Tuple(elems.iter().map(|e| self.expand_type_alias(e)).collect())
|
||||
}
|
||||
Type::Record(fields) => {
|
||||
Type::Record(fields.iter().map(|(n, t)| (n.clone(), self.expand_type_alias(t))).collect())
|
||||
}
|
||||
Type::List(inner) => {
|
||||
Type::List(Box::new(self.expand_type_alias(inner)))
|
||||
}
|
||||
Type::Option(inner) => {
|
||||
Type::Option(Box::new(self.expand_type_alias(inner)))
|
||||
}
|
||||
Type::Versioned { base, version } => {
|
||||
Type::Versioned {
|
||||
base: Box::new(self.expand_type_alias(base)),
|
||||
version: version.clone(),
|
||||
}
|
||||
}
|
||||
// Primitives and type variables stay as-is
|
||||
_ => ty.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unify types with type alias expansion
|
||||
pub fn unify_with_env(t1: &Type, t2: &Type, env: &TypeEnv) -> Result<Substitution, String> {
|
||||
let expanded1 = env.expand_type_alias(t1);
|
||||
let expanded2 = env.expand_type_alias(t2);
|
||||
unify(&expanded1, &expanded2)
|
||||
}
|
||||
|
||||
/// Unification of types
|
||||
|
||||
Reference in New Issue
Block a user