some refactoring and adding assert on lists

This commit is contained in:
Kasey White 2023-01-26 05:20:26 -05:00
parent d4eec1fe79
commit fc88028034
4 changed files with 489 additions and 140 deletions

View File

@ -107,6 +107,11 @@ pub enum Air {
name: String, name: String,
}, },
UnWrapData {
scope: Vec<u64>,
tipo: Arc<Type>,
},
ListAssert { ListAssert {
scope: Vec<u64>, scope: Vec<u64>,
tipo: Arc<Type>, tipo: Arc<Type>,
@ -271,6 +276,7 @@ impl Air {
| Air::Assert { scope, .. } | Air::Assert { scope, .. }
| Air::ListAssert { scope, .. } | Air::ListAssert { scope, .. }
| Air::Let { scope, .. } | Air::Let { scope, .. }
| Air::UnWrapData { scope, .. }
| Air::When { scope, .. } | Air::When { scope, .. }
| Air::Clause { scope, .. } | Air::Clause { scope, .. }
| Air::ListClause { scope, .. } | Air::ListClause { scope, .. }
@ -350,6 +356,7 @@ impl Air {
| Air::BinOp { tipo, .. } | Air::BinOp { tipo, .. }
| Air::Assert { tipo, .. } | Air::Assert { tipo, .. }
| Air::ListAssert { tipo, .. } | Air::ListAssert { tipo, .. }
| Air::UnWrapData { tipo, .. }
| Air::When { tipo, .. } | Air::When { tipo, .. }
| Air::Clause { tipo, .. } | Air::Clause { tipo, .. }
| Air::ListClause { tipo, .. } | Air::ListClause { tipo, .. }

View File

@ -14,7 +14,7 @@ use uplc::{
use crate::{ use crate::{
air::Air, air::Air,
ast::{Clause, Constant, DataType, Pattern, Span, TypedArg, TypedDataType}, ast::{AssignmentKind, Clause, Constant, DataType, Pattern, Span, TypedArg, TypedDataType},
expr::TypedExpr, expr::TypedExpr,
tipo::{PatternConstructor, Type, TypeVar, ValueConstructorVariant}, tipo::{PatternConstructor, Type, TypeVar, ValueConstructorVariant},
}; };
@ -48,6 +48,12 @@ pub struct FunctionAccessKey {
pub variant_name: String, pub variant_name: String,
} }
#[derive(Clone, Debug)]
pub struct AssignmentProperties {
pub value_is_data: bool,
pub kind: AssignmentKind,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum ClauseProperties { pub enum ClauseProperties {
ConstrClause { ConstrClause {
@ -1133,8 +1139,15 @@ pub fn monomorphize(
needs_variant = true; needs_variant = true;
} }
} }
// TODO check on assignment if type is needed Air::UnWrapData { scope, tipo } => {
Air::Let { .. } => {} if tipo.is_generic() {
let mut tipo = tipo.clone();
find_generics_to_replace(&mut tipo, &generic_types);
new_air[index] = Air::UnWrapData { scope, tipo };
needs_variant = true;
}
}
Air::When { Air::When {
scope, scope,
tipo, tipo,
@ -1673,5 +1686,3 @@ pub fn replace_opaque_type(t: &mut Arc<Type>, data_types: IndexMap<DataTypeKey,
} }
} }
} }
pub fn recursive_assert(tipo: &Type, assert_vec: &mut Vec<Air>) {}

View File

@ -6,7 +6,8 @@ use uplc::{
ast::{ ast::{
builder::{ builder::{
self, apply_wrap, choose_list, constr_index_exposer, delayed_choose_list, self, apply_wrap, choose_list, constr_index_exposer, delayed_choose_list,
delayed_if_else, if_else, repeat_tail_list, CONSTR_FIELDS_EXPOSER, CONSTR_GET_FIELD, delayed_if_else, if_else, repeat_tail_list, ASSERT_ON_LIST, CONSTR_FIELDS_EXPOSER,
CONSTR_GET_FIELD,
}, },
Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType, Constant as UplcConstant, Name, NamedDeBruijn, Program, Term, Type as UplcType,
}, },
@ -26,12 +27,12 @@ use crate::{
convert_constants_to_data, convert_data_to_type, convert_type_to_data, get_common_ancestor, convert_constants_to_data, convert_data_to_type, convert_type_to_data, get_common_ancestor,
get_generics_and_type, handle_func_dependencies_ir, handle_recursion_ir, get_generics_and_type, handle_func_dependencies_ir, handle_recursion_ir,
list_access_to_uplc, lookup_data_type_by_tipo, monomorphize, rearrange_clauses, list_access_to_uplc, lookup_data_type_by_tipo, monomorphize, rearrange_clauses,
recursive_assert, replace_opaque_type, wrap_validator_args, ClauseProperties, DataTypeKey, replace_opaque_type, wrap_validator_args, AssignmentProperties, ClauseProperties,
FuncComponents, FunctionAccessKey, DataTypeKey, FuncComponents, FunctionAccessKey,
}, },
expr::TypedExpr, expr::TypedExpr,
tipo::{ tipo::{
ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, self, ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor,
ValueConstructorVariant, ValueConstructorVariant,
}, },
IdGenerator, IdGenerator,
@ -45,6 +46,7 @@ pub struct CodeGenerator<'a> {
module_types: &'a IndexMap<String, TypeInfo>, module_types: &'a IndexMap<String, TypeInfo>,
id_gen: IdGenerator, id_gen: IdGenerator,
needs_field_access: bool, needs_field_access: bool,
used_data_assert_on_list: bool,
zero_arg_functions: IndexMap<FunctionAccessKey, Vec<Air>>, zero_arg_functions: IndexMap<FunctionAccessKey, Vec<Air>>,
} }
@ -63,6 +65,7 @@ impl<'a> CodeGenerator<'a> {
module_types, module_types,
id_gen: IdGenerator::new(), id_gen: IdGenerator::new(),
needs_field_access: false, needs_field_access: false,
used_data_assert_on_list: false,
zero_arg_functions: IndexMap::new(), zero_arg_functions: IndexMap::new(),
} }
} }
@ -104,6 +107,12 @@ impl<'a> CodeGenerator<'a> {
term = wrap_validator_args(term, arguments); term = wrap_validator_args(term, arguments);
term = if wrap_as_validator || self.used_data_assert_on_list {
builder::assert_on_list(term)
} else {
term
};
let mut program = Program { let mut program = Program {
version: (1, 0, 0), version: (1, 0, 0),
term, term,
@ -300,8 +309,10 @@ impl<'a> CodeGenerator<'a> {
&mut pattern_vec, &mut pattern_vec,
&mut value_vec, &mut value_vec,
tipo, tipo,
value_is_data, AssignmentProperties {
*kind, value_is_data,
kind: *kind,
},
scope, scope,
); );
@ -1459,142 +1470,141 @@ impl<'a> CodeGenerator<'a> {
pattern_vec: &mut Vec<Air>, pattern_vec: &mut Vec<Air>,
value_vec: &mut Vec<Air>, value_vec: &mut Vec<Air>,
tipo: &Type, tipo: &Type,
value_is_data: bool, assignment_properties: AssignmentProperties,
kind: AssignmentKind,
scope: Vec<u64>, scope: Vec<u64>,
) { ) {
if assignment_properties.value_is_data && !tipo.is_data() {
value_vec.insert(
0,
Air::UnWrapData {
scope: scope.clone(),
tipo: tipo.clone().into(),
},
);
}
match pattern { match pattern {
Pattern::Int { .. } | Pattern::String { .. } => unreachable!(), Pattern::Int { .. } | Pattern::String { .. } => unreachable!(),
Pattern::Var { name, .. } => { Pattern::Var { name, .. } => {
let mut assert_vec = vec![]; pattern_vec.push(Air::Let {
name: name.clone(),
if matches!(kind, AssignmentKind::Assert) { scope: scope.clone(),
if value_is_data && !tipo.is_data() { });
recursive_assert(tipo, &mut assert_vec);
}
pattern_vec.push(Air::Assert {
scope,
tipo: tipo.clone().into(),
value_is_data,
});
} else {
pattern_vec.push(Air::Let {
name: name.clone(),
scope,
});
}
pattern_vec.append(value_vec); pattern_vec.append(value_vec);
pattern_vec.append(&mut assert_vec);
if matches!(assignment_properties.kind, AssignmentKind::Assert)
&& assignment_properties.value_is_data
&& !tipo.is_data()
{
let mut assert_vec = vec![];
self.recursive_assert_pattern(
pattern,
&mut assert_vec,
value_vec,
tipo,
assignment_properties,
scope,
);
pattern_vec.append(&mut assert_vec);
}
} }
Pattern::VarUsage { .. } => todo!(), Pattern::VarUsage { .. } => todo!(),
Pattern::Assign { .. } => todo!(), Pattern::Assign { .. } => todo!(),
Pattern::Discard { .. } => { Pattern::Discard { .. } => {
pattern_vec.push(Air::Let { pattern_vec.push(Air::Let {
name: "_".to_string(), name: "_".to_string(),
scope, scope: scope.clone(),
}); });
pattern_vec.append(value_vec); pattern_vec.append(value_vec);
}
list @ Pattern::List { .. } => {
self.pattern_ir(
list,
pattern_vec,
value_vec,
tipo,
value_is_data,
kind,
scope,
);
}
Pattern::Constructor {
name: constr_name,
tipo,
..
} => {
let data_type = lookup_data_type_by_tipo(self.data_types.clone(), tipo);
let (index, _) = data_type if matches!(assignment_properties.kind, AssignmentKind::Assert)
.unwrap() && assignment_properties.value_is_data
.constructors && !tipo.is_data()
.iter() {
.enumerate() let mut assert_vec = vec![];
.find(|(_, dt)| &dt.name == constr_name) self.recursive_assert_pattern(
.unwrap(); pattern,
match kind { &mut assert_vec,
AssignmentKind::Let => { value_vec,
self.pattern_ir( tipo,
pattern, assignment_properties,
pattern_vec, scope,
value_vec, );
tipo, pattern_vec.append(&mut assert_vec);
value_is_data,
kind,
scope,
);
}
AssignmentKind::Assert => {
let name_id = self.id_gen.next();
pattern_vec.push(Air::Let {
scope: scope.clone(),
name: format!("__constr_{}", name_id),
});
pattern_vec.append(value_vec);
pattern_vec.push(Air::Assert {
scope: scope.clone(),
tipo: tipo.clone(),
value_is_data,
});
pattern_vec.push(Air::Var {
scope: scope.clone(),
constructor: ValueConstructor::public(
tipo.clone(),
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
),
name: format!("__constr_{}", name_id),
variant_name: String::new(),
});
self.pattern_ir(
pattern,
pattern_vec,
&mut vec![Air::Var {
scope: scope.clone(),
constructor: ValueConstructor::public(
tipo.clone(),
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
),
name: format!("__constr_{}", name_id),
variant_name: String::new(),
}],
tipo,
value_is_data,
kind,
scope,
);
}
AssignmentKind::Check => todo!(),
} }
} }
Pattern::Tuple { .. } => { list @ Pattern::List { .. } => {
self.pattern_ir( if matches!(assignment_properties.kind, AssignmentKind::Assert)
pattern, && assignment_properties.value_is_data
pattern_vec, && !tipo.is_data()
value_vec, {
tipo, self.recursive_assert_pattern(
value_is_data, list,
kind, pattern_vec,
scope, value_vec,
); tipo,
assignment_properties,
scope,
);
} else {
self.pattern_ir(
list,
pattern_vec,
value_vec,
tipo,
assignment_properties,
scope,
);
}
}
constr @ Pattern::Constructor { .. } => {
if matches!(assignment_properties.kind, AssignmentKind::Assert)
&& assignment_properties.value_is_data
&& !tipo.is_data()
{
self.recursive_assert_pattern(
constr,
pattern_vec,
value_vec,
tipo,
assignment_properties,
scope,
);
} else {
self.pattern_ir(
constr,
pattern_vec,
value_vec,
tipo,
assignment_properties,
scope,
);
}
}
tuple @ Pattern::Tuple { .. } => {
if matches!(assignment_properties.kind, AssignmentKind::Assert)
&& assignment_properties.value_is_data
&& !tipo.is_data()
{
self.recursive_assert_pattern(
tuple,
pattern_vec,
value_vec,
tipo,
assignment_properties,
scope,
);
} else {
self.pattern_ir(
tuple,
pattern_vec,
value_vec,
tipo,
assignment_properties,
scope,
);
}
} }
} }
} }
@ -1605,8 +1615,7 @@ impl<'a> CodeGenerator<'a> {
pattern_vec: &mut Vec<Air>, pattern_vec: &mut Vec<Air>,
values: &mut Vec<Air>, values: &mut Vec<Air>,
tipo: &Type, tipo: &Type,
value_is_data: bool, assignment_properties: AssignmentProperties,
kind: AssignmentKind,
scope: Vec<u64>, scope: Vec<u64>,
) { ) {
match pattern { match pattern {
@ -1618,8 +1627,8 @@ impl<'a> CodeGenerator<'a> {
Pattern::Discard { .. } => todo!(), Pattern::Discard { .. } => todo!(),
Pattern::List { elements, tail, .. } => { Pattern::List { elements, tail, .. } => {
let mut elements_vec = vec![]; let mut elements_vec = vec![];
let mut names = vec![]; let mut names = vec![];
for element in elements { for element in elements {
match element { match element {
Pattern::Var { name, .. } => { Pattern::Var { name, .. } => {
@ -1651,8 +1660,7 @@ impl<'a> CodeGenerator<'a> {
&mut elements_vec, &mut elements_vec,
&mut var_vec, &mut var_vec,
&tipo.get_inner_types()[0], &tipo.get_inner_types()[0],
value_is_data, assignment_properties.clone(),
kind,
scope.clone(), scope.clone(),
); );
} }
@ -1668,7 +1676,7 @@ impl<'a> CodeGenerator<'a> {
} }
} }
if matches!(kind, AssignmentKind::Assert) { if matches!(&assignment_properties.kind, AssignmentKind::Assert) {
} else { } else {
pattern_vec.push(Air::ListAccessor { pattern_vec.push(Air::ListAccessor {
names, names,
@ -1743,8 +1751,7 @@ impl<'a> CodeGenerator<'a> {
variant_name: String::new(), variant_name: String::new(),
}], }],
tipo, tipo,
value_is_data, assignment_properties.clone(),
kind,
scope.clone(), scope.clone(),
); );
@ -1811,8 +1818,7 @@ impl<'a> CodeGenerator<'a> {
variant_name: String::new(), variant_name: String::new(),
}], }],
tipo, tipo,
value_is_data, assignment_properties.clone(),
kind,
scope.clone(), scope.clone(),
); );
@ -1883,8 +1889,7 @@ impl<'a> CodeGenerator<'a> {
&mut elements_vec, &mut elements_vec,
&mut var_vec, &mut var_vec,
&tipo.get_inner_types()[0], &tipo.get_inner_types()[0],
value_is_data, assignment_properties.clone(),
kind,
scope.clone(), scope.clone(),
); );
} }
@ -1903,6 +1908,219 @@ impl<'a> CodeGenerator<'a> {
} }
} }
pub fn recursive_assert_pattern(
&mut self,
pattern: &Pattern<PatternConstructor, Arc<Type>>,
pattern_vec: &mut Vec<Air>,
value_vec: &mut Vec<Air>,
tipo: &Type,
assignment_properties: AssignmentProperties,
scope: Vec<u64>,
) {
match pattern {
Pattern::Int { .. } => unreachable!(),
Pattern::String { .. } => unreachable!(),
Pattern::Var { name, .. } => {
self.recursive_assert_tipo(tipo, pattern_vec, name, scope);
}
Pattern::VarUsage {
location,
name,
tipo,
} => todo!(),
Pattern::Assign {
name,
location,
pattern,
} => todo!(),
Pattern::Discard { name, location } => todo!(),
Pattern::List {
location,
elements,
tail,
} => todo!(),
Pattern::Constructor {
is_record,
location,
name,
arguments,
module,
constructor,
with_spread,
tipo,
} => todo!(),
Pattern::Tuple { location, elems } => todo!(),
}
}
fn recursive_assert_tipo(
&mut self,
tipo: &Type,
assert_vec: &mut Vec<Air>,
name: &str,
scope: Vec<u64>,
) {
if tipo.is_bool()
|| tipo.is_bytearray()
|| tipo.is_int()
|| tipo.is_string()
|| tipo.is_void()
{
} else if tipo.is_map() {
self.used_data_assert_on_list = true;
let new_id = self.id_gen.next();
let id_pair = (self.id_gen.next(), self.id_gen.next());
let inner_list_type = &tipo.get_inner_types()[0];
let inner_pair_types = inner_list_type.get_inner_types();
assert_vec.push(Air::Call {
scope: scope.clone(),
count: 2,
tipo: tipo.clone().into(),
});
assert_vec.push(Air::Builtin {
scope: scope.clone(),
func: DefaultFunction::ChooseUnit,
tipo: tipo.clone().into(),
});
assert_vec.push(Air::Call {
scope: scope.clone(),
count: 3,
tipo: tipo.clone().into(),
});
assert_vec.push(Air::Var {
scope: scope.clone(),
constructor: ValueConstructor::public(
tipo.clone().into(),
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
),
name: ASSERT_ON_LIST.to_string(),
variant_name: String::new(),
});
assert_vec.push(Air::Var {
scope: scope.clone(),
constructor: ValueConstructor::public(
tipo.clone().into(),
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
),
name: name.to_owned(),
variant_name: String::new(),
});
assert_vec.push(Air::Fn {
scope: scope.clone(),
params: vec![format!("__pair_{}", new_id)],
});
assert_vec.push(Air::TupleAccessor {
scope: scope.clone(),
names: vec![
format!("__pair_fst_{}", id_pair.0),
format!("__pair_snd_{}", id_pair.1),
],
tipo: inner_list_type.clone(),
});
self.recursive_assert_tipo(
&inner_pair_types[0],
assert_vec,
&format!("__pair_fst_{}", id_pair.0),
scope.clone(),
);
self.recursive_assert_tipo(
&inner_pair_types[1],
assert_vec,
&format!("__pair_snd_{}", id_pair.1),
scope,
);
} else if tipo.is_list() {
self.used_data_assert_on_list = true;
let new_id = self.id_gen.next();
let id_pair = (self.id_gen.next(), self.id_gen.next());
let inner_list_type = &tipo.get_inner_types()[0];
let inner_pair_types = inner_list_type.get_inner_types();
assert_vec.push(Air::Call {
scope: scope.clone(),
count: 2,
tipo: tipo.clone().into(),
});
assert_vec.push(Air::Builtin {
scope: scope.clone(),
func: DefaultFunction::ChooseUnit,
tipo: tipo.clone().into(),
});
assert_vec.push(Air::Call {
scope: scope.clone(),
count: 3,
tipo: tipo.clone().into(),
});
assert_vec.push(Air::Var {
scope: scope.clone(),
constructor: ValueConstructor::public(
tipo.clone().into(),
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
),
name: ASSERT_ON_LIST.to_string(),
variant_name: String::new(),
});
assert_vec.push(Air::Var {
scope: scope.clone(),
constructor: ValueConstructor::public(
tipo.clone().into(),
ValueConstructorVariant::LocalVariable {
location: Span::empty(),
},
),
name: name.to_owned(),
variant_name: String::new(),
});
assert_vec.push(Air::Fn {
scope: scope.clone(),
params: vec![format!("__list_item_{}", new_id)],
});
assert_vec.push(Air::TupleAccessor {
scope: scope.clone(),
names: vec![
format!("__pair_fst_{}", id_pair.0),
format!("__pair_snd_{}", id_pair.1),
],
tipo: inner_list_type.clone(),
});
self.recursive_assert_tipo(
&inner_pair_types[0],
assert_vec,
&format!("__pair_fst_{}", id_pair.0),
scope.clone(),
);
self.recursive_assert_tipo(
&inner_pair_types[1],
assert_vec,
&format!("__pair_snd_{}", id_pair.1),
scope,
)
}
}
fn define_ir(&mut self, ir_stack: &mut Vec<Air>) { fn define_ir(&mut self, ir_stack: &mut Vec<Air>) {
let mut func_components = IndexMap::new(); let mut func_components = IndexMap::new();
let mut func_index_map = IndexMap::new(); let mut func_index_map = IndexMap::new();
@ -2760,6 +2978,15 @@ impl<'a> CodeGenerator<'a> {
tipo: replaced_type, tipo: replaced_type,
}; };
} }
Air::UnWrapData { scope, tipo } => {
let mut replaced_type = tipo.clone();
replace_opaque_type(&mut replaced_type, self.data_types.clone());
ir_stack[index] = Air::UnWrapData {
scope,
tipo: replaced_type,
};
}
_ => {} _ => {}
} }
} }
@ -3692,6 +3919,7 @@ impl<'a> CodeGenerator<'a> {
arg_stack.push(term); arg_stack.push(term);
} }
Air::UnWrapData { scope, tipo } => todo!(),
Air::When { Air::When {
subject_name, tipo, .. subject_name, tipo, ..
} => { } => {

View File

@ -5,6 +5,7 @@ use super::{Constant, Name, Term};
pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer"; pub const CONSTR_FIELDS_EXPOSER: &str = "__constr_fields_exposer";
pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer"; pub const CONSTR_INDEX_EXPOSER: &str = "__constr_index_exposer";
pub const CONSTR_GET_FIELD: &str = "__constr_get_field"; pub const CONSTR_GET_FIELD: &str = "__constr_get_field";
pub const ASSERT_ON_LIST: &str = "__assert_on_list";
pub fn apply_wrap(function: Term<Name>, arg: Term<Name>) -> Term<Name> { pub fn apply_wrap(function: Term<Name>, arg: Term<Name>) -> Term<Name> {
Term::Apply { Term::Apply {
@ -31,6 +32,108 @@ pub fn final_wrapper(term: Term<Name>) -> Term<Name> {
) )
} }
pub fn assert_on_list(term: Term<Name>) -> Term<Name> {
apply_wrap(
Term::Lambda {
parameter_name: Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
},
body: apply_wrap(
Term::Lambda {
parameter_name: Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
},
body: term.into(),
},
apply_wrap(
Term::Var(Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
}),
Term::Var(Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
}),
),
)
.into(),
},
Term::Lambda {
parameter_name: Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
},
body: Term::Lambda {
parameter_name: Name {
text: "list_to_check".to_string(),
unique: 0.into(),
},
body: Term::Lambda {
parameter_name: Name {
text: "check_with".to_string(),
unique: 0.into(),
},
body: delayed_choose_list(
Term::Var(Name {
text: "list_to_check".to_string(),
unique: 0.into(),
}),
Term::Constant(Constant::Unit),
apply_wrap(
apply_wrap(
Term::Builtin(DefaultFunction::ChooseUnit).force_wrap(),
apply_wrap(
Term::Var(Name {
text: "check_with".to_string(),
unique: 0.into(),
}),
apply_wrap(
Term::Builtin(DefaultFunction::HeadList).force_wrap(),
Term::Var(Name {
text: "list_to_check".to_string(),
unique: 0.into(),
}),
),
),
),
apply_wrap(
apply_wrap(
apply_wrap(
Term::Var(Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
}),
Term::Var(Name {
text: ASSERT_ON_LIST.to_string(),
unique: 0.into(),
}),
),
apply_wrap(
Term::Builtin(DefaultFunction::TailList).force_wrap(),
Term::Var(Name {
text: "list_to_check".to_string(),
unique: 0.into(),
}),
),
),
Term::Var(Name {
text: "check_with".to_string(),
unique: 0.into(),
}),
),
),
)
.into(),
}
.into(),
}
.into(),
},
)
}
pub fn constr_fields_exposer(term: Term<Name>) -> Term<Name> { pub fn constr_fields_exposer(term: Term<Name>) -> Term<Name> {
Term::Apply { Term::Apply {
function: Term::Lambda { function: Term::Lambda {