Pattern Trait
The PatternDefinition trait defines the interface for all patterns in Ori.
Trait Definition
pub trait PatternDefinition: Send + Sync {
/// Pattern name (e.g., "map", "filter", "fold")
fn name(&self) -> &str;
/// Argument specifications
fn arguments(&self) -> &[PatternArg];
/// Type check the pattern call
fn type_check(
&self,
args: &[TypedArg],
checker: &mut TypeChecker,
) -> Result<Type, TypeError>;
/// Evaluate the pattern
fn evaluate(
&self,
args: &[EvalArg],
evaluator: &mut Evaluator,
) -> Result<Value, EvalError>;
/// Check if pattern can fuse with next pattern
fn can_fuse(&self, next: &dyn PatternDefinition) -> bool {
false
}
/// Fuse with following pattern (optimization)
fn fuse_with(
&self,
next: &dyn PatternDefinition,
) -> Option<Box<dyn PatternDefinition>> {
None
}
/// Capability requirements
fn capabilities(&self) -> Vec<Capability> {
vec![]
}
}
Argument Specification
pub struct PatternArg {
/// Argument name (e.g., "over", "transform")
pub name: &'static str,
/// Expected type pattern
pub ty: ArgType,
/// Is this argument required?
pub required: bool,
/// Default value if not provided
pub default: Option<Value>,
}
pub enum ArgType {
/// Any type
Any,
/// Specific type
Exact(Type),
/// Generic type variable
TypeVar(TypeVarId),
/// List of T
ListOf(Box<ArgType>),
/// Function from A to B
Function { param: Box<ArgType>, ret: Box<ArgType> },
/// Must be iterable
Iterable,
/// Must be a predicate (returns bool)
Predicate,
}
Example: Map Pattern
pub struct MapPattern;
impl PatternDefinition for MapPattern {
fn name(&self) -> &str {
"map"
}
fn arguments(&self) -> &[PatternArg] {
&[
PatternArg {
name: "over",
ty: ArgType::Iterable,
required: true,
default: None,
},
PatternArg {
name: "transform",
ty: ArgType::Function {
param: Box::new(ArgType::TypeVar(TypeVarId(0))),
ret: Box::new(ArgType::TypeVar(TypeVarId(1))),
},
required: true,
default: None,
},
]
}
fn type_check(
&self,
args: &[TypedArg],
checker: &mut TypeChecker,
) -> Result<Type, TypeError> {
let over_arg = args.find("over")?;
let transform_arg = args.find("transform")?;
// over must be iterable
let elem_ty = checker.extract_element_type(&over_arg.ty)?;
// transform must be function
let (param_ty, ret_ty) = checker.extract_function_type(&transform_arg.ty)?;
// Parameter must match element type
checker.unify(¶m_ty, &elem_ty)?;
// Result is list of return type
Ok(Type::List(Box::new(ret_ty)))
}
fn evaluate(
&self,
args: &[EvalArg],
evaluator: &mut Evaluator,
) -> Result<Value, EvalError> {
let over = args.get("over")?.as_list()?;
let transform = args.get("transform")?.as_function()?;
let result: Vec<Value> = over
.iter()
.map(|item| evaluator.call_function(&transform, vec![item.clone()]))
collect::<Result<_, _>>()?;
Ok(Value::List(Arc::new(result)))
}
fn can_fuse(&self, next: &dyn PatternDefinition) -> bool {
matches!(next.name(), "filter" | "map")
}
fn fuse_with(
&self,
next: &dyn PatternDefinition,
) -> Option<Box<dyn PatternDefinition>> {
match next.name() {
"filter" => Some(Box::new(MapFilterPattern::new(self, next))),
"map" => Some(Box::new(MapMapPattern::new(self, next))),
_ => None,
}
}
}
Example: Fold Pattern
pub struct FoldPattern;
impl PatternDefinition for FoldPattern {
fn name(&self) -> &str {
"fold"
}
fn arguments(&self) -> &[PatternArg] {
&[
PatternArg {
name: "over",
ty: ArgType::Iterable,
required: true,
default: None,
},
PatternArg {
name: "init",
ty: ArgType::TypeVar(TypeVarId(0)), // Accumulator type
required: true,
default: None,
},
PatternArg {
name: "op",
ty: ArgType::Function {
// (acc, elem) -> acc
param: Box::new(ArgType::Exact(Type::Tuple(vec![
Type::TypeVar(TypeVarId(0)),
Type::TypeVar(TypeVarId(1)),
]))),
ret: Box::new(ArgType::TypeVar(TypeVarId(0))),
},
required: true,
default: None,
},
]
}
fn type_check(
&self,
args: &[TypedArg],
checker: &mut TypeChecker,
) -> Result<Type, TypeError> {
let over_ty = args.find("over")?.ty.clone();
let init_ty = args.find("init")?.ty.clone();
let op_ty = args.find("op")?.ty.clone();
let elem_ty = checker.extract_element_type(&over_ty)?;
let (param_ty, ret_ty) = checker.extract_function_type(&op_ty)?;
// op takes (acc, elem), acc matches init
if let Type::Tuple(params) = param_ty {
checker.unify(¶ms[0], &init_ty)?;
checker.unify(¶ms[1], &elem_ty)?;
}
// op returns acc type
checker.unify(&ret_ty, &init_ty)?;
Ok(init_ty)
}
fn evaluate(
&self,
args: &[EvalArg],
evaluator: &mut Evaluator,
) -> Result<Value, EvalError> {
let over = args.get("over")?.as_list()?;
let init = args.get("init")?.clone();
let op = args.get("op")?.as_function()?;
let mut acc = init;
for item in over.iter() {
acc = evaluator.call_function(&op, vec![acc, item.clone()])?;
}
Ok(acc)
}
}
TypedArg and EvalArg
pub struct TypedArg {
pub name: Name,
pub ty: Type,
pub span: Span,
}
impl TypedArg {
pub fn find(args: &[Self], name: &str) -> Option<&Self> {
args.iter().find(|a| a.name.as_str() == name)
}
}
pub struct EvalArg {
pub name: Name,
pub value: Value,
}
impl EvalArg {
pub fn as_list(&self) -> Result<&[Value], EvalError> { ... }
pub fn as_function(&self) -> Result<&FunctionValue, EvalError> { ... }
pub fn as_int(&self) -> Result<i64, EvalError> { ... }
}
Send + Sync Requirements
Patterns must be Send + Sync for:
- Thread-safe registry access
- Parallel compilation
- Concurrent evaluation (parallel pattern)
// All pattern state must be thread-safe
pub struct CachePattern {
cache: Arc<RwLock<HashMap<Value, Value>>>,
}