pub enum TypeF<Ty, RRows, ERows, Te> {
Show 15 variants
Dyn,
Number,
Bool,
String,
Symbol,
ForeignId,
Contract(Te),
Arrow(Ty, Ty),
Var(Ident),
Forall {
var: LocIdent,
var_kind: VarKind,
body: Ty,
},
Enum(ERows),
Record(RRows),
Dict {
type_fields: Ty,
flavour: DictTypeFlavour,
},
Array(Ty),
Wildcard(usize),
}Expand description
A Nickel type.
§Generic representation (functor)
A Nickel type is represented by a tree and is naturally defined in a recursive manner: for
example, one would expect the constructor for function types (Arrow) to look like:
pub enum Type {
Arrow(Box<Type>, Box<Type>),
// ...
}However, TypeF is slightly different, in that it is parametrized by a generic type instead of
using a concrete definition like Box<Types> (forget about rows for now):
pub enum TypeF<Ty /* , .. */> {
Arrow(Ty, Ty),
// ...
}Ty is also called a recursive unfolding throughout the documentation. By defining struct Type(TypeF<Box<Types>>), we get back the original, natural definition.
§Motivation 1: variation on Types
Having a generic definition makes it possible to easily create other types with the same shape
as Type (seen as trees), but with enriched nodes. The typical use-case in Nickel is the
variation on types used by the typechecker. During type inference, the typechecker operates on
trees where each node can either be a concrete type, or a unification variable (a unknown type
to be inferred). Instead of duplicating the whole definition of Type as well as all basic
methods, we can simply have a different recursive definition:
pub enum UnifType {
UnifVar(VarId),
Concrete(TypeF<Box<UnifType> /*, .. */>),
// ..
}We get something that looks like normal Nickel types, except that each node can also be a unification variable as well.
§Motivation 2: recursion schemes
This definition is actually in the style of recursion schemes. Pedantically, TypeF (hence the
F suffix) is a functor, but the formal details aren’t so important: keep in mind that the F
suffix means that the recursive occurrences of subtrees (and enum rows and record rows as well)
are replaced by generic parameters.
The usual motivation for recursion schemes is that they allow for elegant and simple definitions
of recursive transformation over trees (here, TypeF, and more generally anything with an F
suffix) in terms of simple appropriate chaining of map and folding/unfolding operations. A
good example is the definition of crate::ast::typ::Type::traverse. Although crate::ast::Node isn’t currently
defined using functors per se, the way program transformations are written is in the same style
as recursion schemes: we simply define the action of a transformation as a mapping on the
current node, and let the traversal take care of the plumbing of recursion and reconstruction.
§Type parameters
Ty: the recursive unfolding of Nickel typesRRows: the recursive unfolding of record rowsERows: the recursive unfolding of enum rowsTe: the type of a term (used to store contracts)
Variants§
Dyn
The dynamic type, or unitype. Assigned to values whose actual type is not statically known or checked.
Number
A floating point number.
Bool
A boolean.
String
A string literal.
Symbol
A symbol.
See Term::Sealed in nickel_lang_core.
ForeignId
The type of Term::ForeignId.
Contract(Te)
A type created from a user-defined contract.
Arrow(Ty, Ty)
A function.
Var(Ident)
A type variable.
Forall
A forall binder.
Enum(ERows)
An enum type, composed of a sequence of enum rows.
Record(RRows)
A record type, composed of a sequence of record rows.
Dict
A dictionary type.
Array(Ty)
A parametrized array.
Wildcard(usize)
A type wildcard, wrapping an ID unique within a given file.
Implementations§
Source§impl<Ty, RRows, ERows, Te> TypeF<Ty, RRows, ERows, Te>
impl<Ty, RRows, ERows, Te> TypeF<Ty, RRows, ERows, Te>
Sourcepub fn try_map_state<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, S, E>(
self,
f: FTy,
f_rrows: FRRows,
f_erows: FERows,
f_ctr: FTe,
state: &mut S,
) -> Result<TypeF<TyO, RRowsO, ERowsO, TeO>, E>
pub fn try_map_state<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, S, E>( self, f: FTy, f_rrows: FRRows, f_erows: FERows, f_ctr: FTe, state: &mut S, ) -> Result<TypeF<TyO, RRowsO, ERowsO, TeO>, E>
Map functions over the children nodes of a type, when seen as a tree. The mutable state (
S) is threaded through the calls to the mapped functions. Functions are fallible and may
return an error E, which causes try_map_state to return early with the same error.
If we put aside the state and the error (see [RecordRowsF::map), this function makes
TypeF a functor (of arity 3). As hinted by the type signature, this function just
maps on “one-level” of recursion, so to speak.
Take the instantiated version Type, and a type of the form (Dyn -> Dyn) -> (Number -> Dyn). Then, calling try_map_state(f_ty, ..) on this type rows will map f_ty onto (Dyn -> Dyn) and Number -> Dyn because they are direct children of the root Arrow node.
Note that f_ty isn’t mapped onto Dyn and Number recursively: map isn’t a recursive
operation. It’s however a building block to express recursive operations: as an example,
see crate::ast::typ::RecordRows::traverse.
Since TypeF may contain record rows and enum rows as well, f_rrows and f_erows are
required to know how to map on record and enum types respectively.
Sourcepub fn try_map<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, E>(
self,
f: FTy,
f_rrows: FRRows,
f_erows: FERows,
f_ctr: FTe,
) -> Result<TypeF<TyO, RRowsO, ERowsO, TeO>, E>
pub fn try_map<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, E>( self, f: FTy, f_rrows: FRRows, f_erows: FERows, f_ctr: FTe, ) -> Result<TypeF<TyO, RRowsO, ERowsO, TeO>, E>
Variant of try_map_state without threaded state.
Sourcepub fn map_state<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, S>(
self,
f: FTy,
f_rrows: FRRows,
f_erows: FERows,
f_ctr: FTe,
state: &mut S,
) -> TypeF<TyO, RRowsO, ERowsO, TeO>
pub fn map_state<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, S>( self, f: FTy, f_rrows: FRRows, f_erows: FERows, f_ctr: FTe, state: &mut S, ) -> TypeF<TyO, RRowsO, ERowsO, TeO>
Variant of try_map_state with infallible functions.
Sourcepub fn map<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe>(
self,
f: FTy,
f_rrows: FRRows,
f_erows: FERows,
f_ctr: FTe,
) -> TypeF<TyO, RRowsO, ERowsO, TeO>
pub fn map<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe>( self, f: FTy, f_rrows: FRRows, f_erows: FERows, f_ctr: FTe, ) -> TypeF<TyO, RRowsO, ERowsO, TeO>
Variant of try_map_state without threaded state and with infallible functions.
pub fn is_wildcard(&self) -> bool
pub fn is_contract(&self) -> bool
Trait Implementations§
Source§impl<Ty, RRows, ERows, Te> CloneTo for TypeF<Ty, RRows, ERows, Te>
impl<Ty, RRows, ERows, Te> CloneTo for TypeF<Ty, RRows, ERows, Te>
Source§type Data<'a> = TypeF<<Ty as CloneTo>::Data<'a>, <RRows as CloneTo>::Data<'a>, <ERows as CloneTo>::Data<'a>, <Te as CloneTo>::Data<'a>>
type Data<'a> = TypeF<<Ty as CloneTo>::Data<'a>, <RRows as CloneTo>::Data<'a>, <ERows as CloneTo>::Data<'a>, <Te as CloneTo>::Data<'a>>
Self, be we need associated types to make Rust understand that Self is
always parametric over the 'ast lifetime. We’re using GATs to emulate higher-kinded
types.Source§impl<'ast> From<TypeF<&'ast Type<'ast>, RecordRows<'ast>, EnumRows<'ast>, &'ast Ast<'ast>>> for Type<'ast>
impl<'ast> From<TypeF<&'ast Type<'ast>, RecordRows<'ast>, EnumRows<'ast>, &'ast Ast<'ast>>> for Type<'ast>
Source§impl<Ty: PartialEq, RRows: PartialEq, ERows: PartialEq, Te: PartialEq> PartialEq for TypeF<Ty, RRows, ERows, Te>
impl<Ty: PartialEq, RRows: PartialEq, ERows: PartialEq, Te: PartialEq> PartialEq for TypeF<Ty, RRows, ERows, Te>
impl<Ty: Eq, RRows: Eq, ERows: Eq, Te: Eq> Eq for TypeF<Ty, RRows, ERows, Te>
impl<Ty, RRows, ERows, Te> StructuralPartialEq for TypeF<Ty, RRows, ERows, Te>
Auto Trait Implementations§
impl<Ty, RRows, ERows, Te> Freeze for TypeF<Ty, RRows, ERows, Te>
impl<Ty, RRows, ERows, Te> RefUnwindSafe for TypeF<Ty, RRows, ERows, Te>
impl<Ty, RRows, ERows, Te> Send for TypeF<Ty, RRows, ERows, Te>
impl<Ty, RRows, ERows, Te> Sync for TypeF<Ty, RRows, ERows, Te>
impl<Ty, RRows, ERows, Te> Unpin for TypeF<Ty, RRows, ERows, Te>
impl<Ty, RRows, ERows, Te> UnsafeUnpin for TypeF<Ty, RRows, ERows, Te>
impl<Ty, RRows, ERows, Te> UnwindSafe for TypeF<Ty, RRows, ERows, Te>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more