Skip to main content

nickel_lang_parser/
typ.rs

1//! Nickel static types.
2//!
3//! The type system of Nickel is comprised of primitive types, arrays, records, functions, and
4//! opaque types (contracts). This is a structural type system with row polymorphism for both
5//! records and enums.
6//!
7//! ## Record types (rows)
8//!
9//! A row type for a record is represented as a linked list of pairs `(id, type)` indicating the
10//! name and the type of each field. Row-polymorphism means that the tail of this list can be a
11//! type variable which can be abstracted over, leaving the row open for future extension. A simple
12//! illustration is record field access:
13//!
14//! ```nickel
15//! let f : forall a. { some_field : Number; a} -> Number =
16//!   fun record => record.some_field
17//! ```
18//!
19//! The type `{ some_field: Number; a }` indicates that an argument to this function must have at
20//! least the field `some_field` of type `Number`, but may contain other fields (or not).
21//!
22//! ## Dictionaries
23//!
24//! A dictionary type `{ _ : Type }` represents a record whose fields all have the type `Type`. The
25//! count and the name of the fields aren't constrained. Dictionaries can be mapped over, extended,
26//! shrinked and accessed in a type-safe manner.
27//!
28//! # Enum types
29//!
30//! An enum type is also a row type where each element is a tag, such as `[| 'foo, 'bar, 'baz |]`.
31//! This type represent values that can be either `'foo`, `'bar` or `'baz`. Enums support row
32//! polymorphism as well.
33//!
34//! # Contracts
35//!
36//! To each type corresponds a contract, which is equivalent to a Nickel function which checks at
37//! runtime that its argument is of the given type. Contract checks are introduced by a contract
38//! annotation or propagated via merging. They ensure sane interaction between typed and untyped
39//! parts.
40//!
41//! Conversely, any Nickel term seen as a contract corresponds to a type, which is opaque and can
42//! only be equated with itself.
43
44// Some doc links point to, e.g. Type::traverse. rustdoc doesn't support fully-qualified syntax,
45// so this import was the only way I could figure out to have `Type::traverse` in scope.
46#[cfg(doc)]
47use crate::traverse::TraverseAlloc;
48
49use crate::{
50    error::{ParseError, ParseErrors},
51    identifier::{Ident, LocIdent},
52};
53
54use std::{collections::HashSet, convert::Infallible};
55
56/// A record row, mapping an identifier to a type. A record type is a dictionary mapping
57/// identifiers to Nickel type. Record types are represented as sequences of `RecordRowF`, ending
58/// potentially with a type variable or `Dyn` in tail position.
59///
60/// # Type parameters
61///
62/// As other types with the `F` suffix, this type is parametrized by one or more recursive
63/// unfoldings (here, `Ty` for `TypeF`). See [`TypeF`] for more details.
64#[derive(Clone, PartialEq, Eq, Debug)]
65pub struct RecordRowF<Ty> {
66    pub id: LocIdent,
67    pub typ: Ty,
68}
69
70/// An enum row, mapping an identifier to an optional type. If the associated type is `None`, this
71/// represent a bare (unapplied) enum tag such as `'foo`. If the enum is applied (a variant), the
72/// type is store in `typ`. An enum type is a set of enum rows, represented as a sequence of
73/// `EnumRow`s, ending potentially with a type variable tail position.
74///
75/// # Type parameters
76///
77/// As other types with the `F` suffix, this type is parametrized by one or more recursive
78/// unfoldings (here, `Ty` for `TypeF`). See [`TypeF`] for more details.
79#[derive(Clone, PartialEq, Eq, Debug)]
80pub struct EnumRowF<Ty> {
81    pub id: LocIdent,
82    pub typ: Option<Ty>,
83}
84
85/// Generic sequence of record rows potentially with a type variable or `Dyn` in tail position.
86///
87/// As other types with the `F` suffix, this type is parametrized by one or more recursive
88/// unfoldings. See [`TypeF`] for more details.
89///
90/// # Type parameters
91///
92/// - `Ty` is the recursive unfolding of a Nickel type stored inside one row. In practice, a
93///   wrapper around an instantiation of `TypeF`.
94/// - `RRows` is the recursive unfolding of record rows (the tail of this row sequence). In
95///   practice, a wrapper around an instantiation of `RecordRowsF`.
96#[derive(Clone, PartialEq, Eq, Debug)]
97pub enum RecordRowsF<Ty, RRows> {
98    Empty,
99    Extend { row: RecordRowF<Ty>, tail: RRows },
100    TailVar(LocIdent),
101    TailDyn,
102}
103
104/// Generic sequence of enum rows potentially with a type variable in tail position.
105///
106/// As other types with the `F` suffix, this type is parametrized by one or more recursive
107/// unfoldings. See [`TypeF`] for more details.
108///
109/// # Type parameters
110///
111/// - `ERows` is the recursive unfolding of enum rows (the tail of this row sequence). In practice,
112///   a wrapper around `EnumRowsF`.
113#[derive(Clone, PartialEq, Eq, Debug)]
114pub enum EnumRowsF<Ty, ERows> {
115    Empty,
116    Extend { row: EnumRowF<Ty>, tail: ERows },
117    TailVar(LocIdent),
118}
119
120/// The kind of a quantified type variable.
121///
122/// Nickel uses several forms of polymorphism. A type variable can be substituted for a type, as in
123/// `id : forall a. a -> a`, for record rows as in `access_foo : forall a . {foo : Number; a} ->
124/// Number}`, or for enum rows. This information is implicit in the source syntax: we don't require
125/// users to write e.g. `forall a :: Type` or `forall a :: Rows`. But the kind of a variable is
126/// required for the typechecker. It is thus determined during parsing and stored as `VarKind` where
127/// type variables are introduced, that is, on forall quantifiers.
128#[derive(Clone, PartialEq, Eq, Debug, Default)]
129pub enum VarKind {
130    #[default]
131    Type,
132    /// `excluded` keeps track of which rows appear somewhere alongside the tail, and therefore
133    /// cannot appear in the tail. For instance `forall r. { ; r } -> { x : Number ; r }` assumes
134    EnumRows { excluded: HashSet<Ident> },
135    /// Same as for [Self::EnumRows].
136    RecordRows { excluded: HashSet<Ident> },
137}
138
139/// Equivalent to `std::mem::Discriminant<VarKind>`, but we can do things like match on it
140// TODO: this seems overly complicated, and it's anyways more space-efficient to store the
141// `excluded` information separately like we do in the `State` field constr. Probably we can store
142// it that way during parsing too.
143#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
144pub enum VarKindDiscriminant {
145    Type,
146    EnumRows,
147    RecordRows,
148}
149
150impl From<&VarKind> for VarKindDiscriminant {
151    fn from(vk: &VarKind) -> Self {
152        match vk {
153            VarKind::Type => VarKindDiscriminant::Type,
154            VarKind::EnumRows { .. } => VarKindDiscriminant::EnumRows,
155            VarKind::RecordRows { .. } => VarKindDiscriminant::RecordRows,
156        }
157    }
158}
159
160/// Flavour of a dictionary type. There are currently two way of writing a dictionary type-ish
161/// object: as a dictionary contract `{_ | T}` or as a dictionary type `{_ : T}`. Ideally, the
162/// former wouldn't even be a type but mostly syntactic sugar for a builtin contract application,
163/// or maybe a proper AST node.
164///
165/// However, the LSP needs to handle both dictionary types and contracts specifically in order to
166/// provide good completion. As we added dictionary contract just before 1.0 to fix a non trivial
167/// issue with respect to polymorphic contracts ([GitHub
168/// issue](https://github.com/tweag/nickel/issues/1228)), the solution to just tweak dictionary
169/// types to be able to hold both kinds - generating a different contract  - seemed to be the
170/// simplest to preserve the user experience (LSP, handling of dictionary when reporting a contract
171/// blame, etc.).
172///
173/// Dictionary contracts might get a proper AST node later on.
174#[derive(Clone, Debug, Copy, Eq, PartialEq)]
175pub enum DictTypeFlavour {
176    /// Dictionary type (`{_ : T}`)
177    Type,
178    /// Dictionary contract (`{_ | T}`)
179    Contract,
180}
181
182/// A Nickel type.
183///
184/// # Generic representation (functor)
185///
186/// A Nickel type is represented by a tree and is naturally defined in a recursive manner: for
187/// example, one would expect the constructor for function types (`Arrow`) to look like:
188///
189/// ```rust
190/// pub enum Type {
191///      Arrow(Box<Type>, Box<Type>),
192///      // ...
193/// }
194/// ```
195///
196/// However, `TypeF` is slightly different, in that it is parametrized by a generic type instead of
197/// using a concrete definition like `Box<Types>` (forget about rows for now):
198///
199/// ```rust
200/// pub enum TypeF<Ty /* , .. */> {
201///      Arrow(Ty, Ty),
202///      // ...
203/// }
204/// ```
205///
206/// `Ty` is also called a recursive unfolding throughout the documentation. By defining `struct
207/// Type(TypeF<Box<Types>>)`, we get back the original, natural definition.
208///
209/// ## Motivation 1: variation on `Types`
210///
211/// Having a generic definition makes it possible to easily create other types with the _same shape_
212/// as `Type` (seen as trees), but with enriched nodes. The typical use-case in Nickel is the
213/// variation on types used by the typechecker. During type inference, the typechecker operates on
214/// trees where each node can either be a concrete type, or a unification variable (a unknown type
215/// to be inferred). Instead of duplicating the whole definition of `Type` as well as all basic
216/// methods, we can simply have a different recursive definition:
217///
218/// ```
219/// # // phony declarations to make this example pass the tests
220/// # type VarId = ();
221/// # type TypeF<T> = T;
222///
223/// pub enum UnifType {
224///    UnifVar(VarId),
225///    Concrete(TypeF<Box<UnifType> /*, .. */>),
226///    // ..
227///  }
228/// ```
229///
230/// We get something that looks like normal Nickel types, except that each node can also be a
231/// unification variable as well.
232///
233/// ## Motivation 2: recursion schemes
234///
235/// This definition is actually in the style of recursion schemes. Pedantically, `TypeF` (hence the
236/// `F` suffix) is a functor, but the formal details aren't so important: keep in mind that the `F`
237/// suffix means that the recursive occurrences of subtrees (and enum rows and record rows as well)
238/// are replaced by generic parameters.
239///
240/// The usual motivation for recursion schemes is that they allow for elegant and simple definitions
241/// of recursive transformation over trees (here, `TypeF`, and more generally anything with an `F`
242/// suffix) in terms of simple appropriate chaining of `map` and folding/unfolding operations. A
243/// good example is the definition of [crate::ast::typ::Type::traverse]. Although [crate::ast::Node] isn't currently
244/// defined using functors per se, the way program transformations are written is in the same style
245/// as recursion schemes: we simply define the action of a transformation as a mapping on the
246/// current node, and let the traversal take care of the plumbing of recursion and reconstruction.
247///
248/// ## Type parameters
249///
250/// - `Ty`: the recursive unfolding of Nickel types
251/// - `RRows`: the recursive unfolding of record rows
252/// - `ERows`: the recursive unfolding of enum rows
253/// - `Te`: the type of a term (used to store contracts)
254#[derive(Clone, PartialEq, Eq, Debug)]
255pub enum TypeF<Ty, RRows, ERows, Te> {
256    /// The dynamic type, or unitype. Assigned to values whose actual type is not statically known
257    /// or checked.
258    Dyn,
259    /// A floating point number.
260    Number,
261    /// A boolean.
262    Bool,
263    /// A string literal.
264    String,
265    /// A symbol.
266    ///
267    /// See `Term::Sealed` in `nickel_lang_core`.
268    Symbol,
269    /// The type of `Term::ForeignId`.
270    ForeignId,
271    /// A type created from a user-defined contract.
272    Contract(Te),
273    /// A function.
274    Arrow(Ty, Ty),
275    /// A type variable.
276    Var(Ident),
277    /// A forall binder.
278    Forall {
279        var: LocIdent,
280        var_kind: VarKind,
281        body: Ty,
282    },
283
284    /// An enum type, composed of a sequence of enum rows.
285    Enum(ERows),
286    /// A record type, composed of a sequence of record rows.
287    Record(RRows),
288    /// A dictionary type.
289    Dict {
290        type_fields: Ty,
291        flavour: DictTypeFlavour,
292    },
293    /// A parametrized array.
294    Array(Ty),
295    /// A type wildcard, wrapping an ID unique within a given file.
296    Wildcard(usize),
297}
298
299impl<Ty, RRows> RecordRowsF<Ty, RRows> {
300    /// Map functions over the children nodes of record rows, when seen as a tree. The mutable
301    /// state ( `S`) is threaded through the calls to the mapped functions. Functions are fallible
302    /// and may return an error `E`, which causes `try_map_state` to return early with the same
303    /// error.
304    ///
305    /// If we put aside the state and the error (see [RecordRowsF::map), this function makes
306    /// `RecordRowsF` a functor (of arity 2). As hinted by the type signature, this function just
307    /// maps on "one-level" of recursion, so to speak. Take the instantiated version `RecordRows`,
308    /// and record rows of the form `{foo : T, bar: U, baz: V}`. Then, calling `try_map_state(f_ty,
309    /// f_rrows, state)` on these rows will map `f_ty` onto `T` and `f_rrows` onto `{bar : U, baz:
310    /// V}`.
311    ///
312    /// Note that `f_ty` isn't mapped onto `U` and `V` recursively: map isn't a recursive
313    /// operation. It's however a building block to express recursive operations: as an example,
314    /// see [crate::ast::typ::RecordRows::traverse].
315    pub fn try_map_state<TyO, RRowsO, FTy, FRRows, S, E>(
316        self,
317        mut f_ty: FTy,
318        mut f_rrows: FRRows,
319        state: &mut S,
320    ) -> Result<RecordRowsF<TyO, RRowsO>, E>
321    where
322        FTy: FnMut(Ty, &mut S) -> Result<TyO, E>,
323        FRRows: FnMut(RRows, &mut S) -> Result<RRowsO, E>,
324    {
325        match self {
326            RecordRowsF::Empty => Ok(RecordRowsF::Empty),
327            RecordRowsF::Extend {
328                row: RecordRowF { id, typ },
329                tail,
330            } => Ok(RecordRowsF::Extend {
331                row: RecordRowF {
332                    id,
333                    typ: f_ty(typ, state)?,
334                },
335                tail: f_rrows(tail, state)?,
336            }),
337            RecordRowsF::TailDyn => Ok(RecordRowsF::TailDyn),
338            RecordRowsF::TailVar(id) => Ok(RecordRowsF::TailVar(id)),
339        }
340    }
341
342    /// Variant of `try_map_state` without threaded state.
343    pub fn try_map<TyO, RRowsO, FTy, FRRows, E>(
344        self,
345        mut f_ty: FTy,
346        mut f_rrows: FRRows,
347    ) -> Result<RecordRowsF<TyO, RRowsO>, E>
348    where
349        FTy: FnMut(Ty) -> Result<TyO, E>,
350        FRRows: FnMut(RRows) -> Result<RRowsO, E>,
351    {
352        let f_ty_lifted = |rrow: Ty, _: &mut ()| -> Result<TyO, E> { f_ty(rrow) };
353        let f_rrows_lifted = |rrows: RRows, _: &mut ()| -> Result<RRowsO, E> { f_rrows(rrows) };
354
355        self.try_map_state(f_ty_lifted, f_rrows_lifted, &mut ())
356    }
357
358    /// Variant of `try_map_state` with infallible functions.
359    pub fn map_state<TyO, RRowsO, FTy, FRRows, S>(
360        self,
361        mut f_ty: FTy,
362        mut f_rrows: FRRows,
363        state: &mut S,
364    ) -> RecordRowsF<TyO, RRowsO>
365    where
366        FTy: FnMut(Ty, &mut S) -> TyO,
367        FRRows: FnMut(RRows, &mut S) -> RRowsO,
368    {
369        let f_ty_lifted = |rrow: Ty, state: &mut S| -> Result<TyO, ()> { Ok(f_ty(rrow, state)) };
370        let f_rrows_lifted =
371            |rrows: RRows, state: &mut S| -> Result<RRowsO, ()> { Ok(f_rrows(rrows, state)) };
372
373        self.try_map_state(f_ty_lifted, f_rrows_lifted, state)
374            .unwrap()
375    }
376
377    /// Variant of `try_map_state` without threaded state and with infallible functions.
378    pub fn map<TyO, RRowsO, FTy, FRRows>(
379        self,
380        mut f_ty: FTy,
381        mut f_rrows: FRRows,
382    ) -> RecordRowsF<TyO, RRowsO>
383    where
384        FTy: FnMut(Ty) -> TyO,
385        FRRows: FnMut(RRows) -> RRowsO,
386    {
387        let f_ty_lifted = |rrow: Ty| -> Result<TyO, Infallible> { Ok(f_ty(rrow)) };
388        let f_rrows_lifted = |rrows: RRows| -> Result<RRowsO, Infallible> { Ok(f_rrows(rrows)) };
389        self.try_map(f_ty_lifted, f_rrows_lifted).unwrap()
390    }
391}
392
393impl<Ty, ERows> EnumRowsF<Ty, ERows> {
394    /// Map functions over the tail of enum rows. The mutable state ( `S`) is threaded through the
395    /// calls to the mapped function. The function is fallible and may return an error `E`, which
396    /// causes `try_map_state` to return early with the same error.
397    ///
398    /// If we put aside the state and the error (see [EnumRowsF::map), this function makes
399    /// `EnumRowsF` a functor. As hinted by the type signature, this function just maps on
400    /// "one-level" of recursion, so to speak. Take the instantiated version `EnumRows`, and
401    /// enum rows of the form ``[| 'foo, 'bar, 'baz |]``. Then, calling `try_map_state(f_erows,
402    /// state)` on these rows will map `f_erows` onto ``[| 'bar, 'baz |]``.
403    ///
404    /// Note that `f_erows` is just mapped once. Map isn't a recursive operation. It's however a
405    /// building block to express recursive operations: as an example, see
406    /// [crate::ast::typ::RecordRows::traverse].
407    pub fn try_map_state<TyO, ERowsO, FTy, FERows, S, E>(
408        self,
409        mut f_ty: FTy,
410        f_erows: FERows,
411        state: &mut S,
412    ) -> Result<EnumRowsF<TyO, ERowsO>, E>
413    where
414        FTy: FnMut(Ty, &mut S) -> Result<TyO, E>,
415        FERows: FnOnce(ERows, &mut S) -> Result<ERowsO, E>,
416    {
417        match self {
418            EnumRowsF::Empty => Ok(EnumRowsF::Empty),
419            EnumRowsF::Extend {
420                row: EnumRowF { id, typ },
421                tail,
422            } => Ok(EnumRowsF::Extend {
423                row: EnumRowF {
424                    id,
425                    typ: typ.map(|ty| f_ty(ty, state)).transpose()?,
426                },
427                tail: f_erows(tail, state)?,
428            }),
429            EnumRowsF::TailVar(id) => Ok(EnumRowsF::TailVar(id)),
430        }
431    }
432
433    /// Variant of `try_map_state` without threaded state.
434    pub fn try_map<TyO, ERowsO, FTy, FERows, E>(
435        self,
436        mut f_ty: FTy,
437        mut f_erows: FERows,
438    ) -> Result<EnumRowsF<TyO, ERowsO>, E>
439    where
440        FTy: FnMut(Ty) -> Result<TyO, E>,
441        FERows: FnMut(ERows) -> Result<ERowsO, E>,
442    {
443        let f_ty_lifted = |erow: Ty, _: &mut ()| -> Result<TyO, E> { f_ty(erow) };
444        let f_erows_lifted = |erows: ERows, _: &mut ()| -> Result<ERowsO, E> { f_erows(erows) };
445
446        self.try_map_state(f_ty_lifted, f_erows_lifted, &mut ())
447    }
448
449    /// Variant of `try_map_state` with infallible functions.
450    pub fn map_state<TyO, ERowsO, FTy, FERows, S>(
451        self,
452        mut f_ty: FTy,
453        mut f_erows: FERows,
454        state: &mut S,
455    ) -> EnumRowsF<TyO, ERowsO>
456    where
457        FTy: FnMut(Ty, &mut S) -> TyO,
458        FERows: FnMut(ERows, &mut S) -> ERowsO,
459    {
460        let f_ty_lifted = |erow: Ty, state: &mut S| -> Result<TyO, ()> { Ok(f_ty(erow, state)) };
461        let f_erows_lifted =
462            |erows: ERows, state: &mut S| -> Result<ERowsO, ()> { Ok(f_erows(erows, state)) };
463        self.try_map_state(f_ty_lifted, f_erows_lifted, state)
464            .unwrap()
465    }
466
467    /// Variant of `try_map_state` without threaded state and with infallible functions.
468    pub fn map<TyO, ERowsO, FTy, FERows>(
469        self,
470        mut f_ty: FTy,
471        mut f_erows: FERows,
472    ) -> EnumRowsF<TyO, ERowsO>
473    where
474        FTy: FnMut(Ty) -> TyO,
475        FERows: FnMut(ERows) -> ERowsO,
476    {
477        let f_ty_lifted = |erow: Ty| -> Result<TyO, Infallible> { Ok(f_ty(erow)) };
478        let f_erows_lifted = |erows: ERows| -> Result<ERowsO, Infallible> { Ok(f_erows(erows)) };
479
480        self.try_map(f_ty_lifted, f_erows_lifted).unwrap()
481    }
482}
483
484impl<Ty, RRows, ERows, Te> TypeF<Ty, RRows, ERows, Te> {
485    /// Map functions over the children nodes of a type, when seen as a tree. The mutable state (
486    /// `S`) is threaded through the calls to the mapped functions. Functions are fallible and may
487    /// return an error `E`, which causes `try_map_state` to return early with the same error.
488    ///
489    /// If we put aside the state and the error (see [RecordRowsF::map), this function makes
490    /// `TypeF` a functor (of arity 3). As hinted by the type signature, this function just
491    /// maps on "one-level" of recursion, so to speak.
492    ///
493    /// Take the instantiated version `Type`, and a type of the form `(Dyn -> Dyn) -> (Number ->
494    /// Dyn)`. Then, calling `try_map_state(f_ty, ..)` on this type rows will map `f_ty` onto `(Dyn
495    /// -> Dyn)` and `Number -> Dyn` because they are direct children of the root `Arrow` node.
496    ///
497    /// Note that `f_ty` isn't mapped onto `Dyn` and `Number` recursively: map isn't a recursive
498    /// operation. It's however a building block to express recursive operations: as an example,
499    /// see [crate::ast::typ::RecordRows::traverse].
500    ///
501    /// Since `TypeF` may contain record rows and enum rows as well, `f_rrows` and `f_erows` are
502    /// required to know how to map on record and enum types respectively.
503    pub fn try_map_state<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, S, E>(
504        self,
505        mut f: FTy,
506        mut f_rrows: FRRows,
507        mut f_erows: FERows,
508        mut f_ctr: FTe,
509        state: &mut S,
510    ) -> Result<TypeF<TyO, RRowsO, ERowsO, TeO>, E>
511    where
512        FTy: FnMut(Ty, &mut S) -> Result<TyO, E>,
513        FRRows: FnMut(RRows, &mut S) -> Result<RRowsO, E>,
514        FERows: FnMut(ERows, &mut S) -> Result<ERowsO, E>,
515        FTe: FnMut(Te, &mut S) -> Result<TeO, E>,
516    {
517        match self {
518            TypeF::Dyn => Ok(TypeF::Dyn),
519            TypeF::Number => Ok(TypeF::Number),
520            TypeF::Bool => Ok(TypeF::Bool),
521            TypeF::String => Ok(TypeF::String),
522            TypeF::ForeignId => Ok(TypeF::ForeignId),
523            TypeF::Symbol => Ok(TypeF::Symbol),
524            TypeF::Contract(t) => Ok(TypeF::Contract(f_ctr(t, state)?)),
525            TypeF::Arrow(dom, codom) => Ok(TypeF::Arrow(f(dom, state)?, f(codom, state)?)),
526            TypeF::Var(i) => Ok(TypeF::Var(i)),
527            TypeF::Forall {
528                var,
529                var_kind,
530                body,
531            } => Ok(TypeF::Forall {
532                var,
533                var_kind,
534                body: f(body, state)?,
535            }),
536            TypeF::Enum(erows) => Ok(TypeF::Enum(f_erows(erows, state)?)),
537            TypeF::Record(rrows) => Ok(TypeF::Record(f_rrows(rrows, state)?)),
538            TypeF::Dict {
539                type_fields,
540                flavour: attrs,
541            } => Ok(TypeF::Dict {
542                type_fields: f(type_fields, state)?,
543                flavour: attrs,
544            }),
545            TypeF::Array(t) => Ok(TypeF::Array(f(t, state)?)),
546            TypeF::Wildcard(i) => Ok(TypeF::Wildcard(i)),
547        }
548    }
549
550    /// Variant of `try_map_state` without threaded state.
551    pub fn try_map<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, E>(
552        self,
553        mut f: FTy,
554        mut f_rrows: FRRows,
555        mut f_erows: FERows,
556        mut f_ctr: FTe,
557    ) -> Result<TypeF<TyO, RRowsO, ERowsO, TeO>, E>
558    where
559        FTy: FnMut(Ty) -> Result<TyO, E>,
560        FRRows: FnMut(RRows) -> Result<RRowsO, E>,
561        FERows: FnMut(ERows) -> Result<ERowsO, E>,
562        FTe: FnMut(Te) -> Result<TeO, E>,
563    {
564        let f_lifted = |ty: Ty, _: &mut ()| -> Result<TyO, E> { f(ty) };
565        let f_rrows_lifted = |rrows: RRows, _: &mut ()| -> Result<RRowsO, E> { f_rrows(rrows) };
566        let f_erows_lifted = |erows: ERows, _: &mut ()| -> Result<ERowsO, E> { f_erows(erows) };
567        let f_ctr_lifted = |ctr: Te, _: &mut ()| -> Result<TeO, E> { f_ctr(ctr) };
568
569        self.try_map_state(
570            f_lifted,
571            f_rrows_lifted,
572            f_erows_lifted,
573            f_ctr_lifted,
574            &mut (),
575        )
576    }
577
578    /// Variant of `try_map_state` with infallible functions.
579    pub fn map_state<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, S>(
580        self,
581        mut f: FTy,
582        mut f_rrows: FRRows,
583        mut f_erows: FERows,
584        mut f_ctr: FTe,
585        state: &mut S,
586    ) -> TypeF<TyO, RRowsO, ERowsO, TeO>
587    where
588        FTy: FnMut(Ty, &mut S) -> TyO,
589        FRRows: FnMut(RRows, &mut S) -> RRowsO,
590        FERows: FnMut(ERows, &mut S) -> ERowsO,
591        FTe: FnMut(Te, &mut S) -> TeO,
592    {
593        let f_lifted = |ty: Ty, state: &mut S| -> Result<TyO, Infallible> { Ok(f(ty, state)) };
594        let f_rrows_lifted = |rrows: RRows, state: &mut S| -> Result<RRowsO, Infallible> {
595            Ok(f_rrows(rrows, state))
596        };
597        let f_erows_lifted = |erows: ERows, state: &mut S| -> Result<ERowsO, Infallible> {
598            Ok(f_erows(erows, state))
599        };
600        let f_ctr_lifted =
601            |ctr: Te, state: &mut S| -> Result<TeO, Infallible> { Ok(f_ctr(ctr, state)) };
602
603        self.try_map_state(
604            f_lifted,
605            f_rrows_lifted,
606            f_erows_lifted,
607            f_ctr_lifted,
608            state,
609        )
610        .unwrap()
611    }
612
613    /// Variant of `try_map_state` without threaded state and with infallible functions.
614    pub fn map<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe>(
615        self,
616        mut f: FTy,
617        mut f_rrows: FRRows,
618        mut f_erows: FERows,
619        mut f_ctr: FTe,
620    ) -> TypeF<TyO, RRowsO, ERowsO, TeO>
621    where
622        FTy: FnMut(Ty) -> TyO,
623        FRRows: FnMut(RRows) -> RRowsO,
624        FERows: FnMut(ERows) -> ERowsO,
625        FTe: FnMut(Te) -> TeO,
626    {
627        let f_lifted = |ty: Ty, _: &mut ()| -> TyO { f(ty) };
628        let f_rrows_lifted = |rrows: RRows, _: &mut ()| -> RRowsO { f_rrows(rrows) };
629        let f_erows_lifted = |erows: ERows, _: &mut ()| -> ERowsO { f_erows(erows) };
630        let f_ctr_lifted = |ctr: Te, _: &mut ()| -> TeO { f_ctr(ctr) };
631
632        self.map_state(
633            f_lifted,
634            f_rrows_lifted,
635            f_erows_lifted,
636            f_ctr_lifted,
637            &mut (),
638        )
639    }
640
641    pub fn is_wildcard(&self) -> bool {
642        matches!(self, TypeF::Wildcard(_))
643    }
644
645    pub fn is_contract(&self) -> bool {
646        matches!(self, TypeF::Contract(_))
647    }
648}
649
650#[derive(Clone, Debug)]
651pub struct UnboundTypeVariableError(pub LocIdent);
652
653impl From<UnboundTypeVariableError> for ParseError {
654    fn from(err: UnboundTypeVariableError) -> Self {
655        ParseError::UnboundTypeVariables(vec![err.0])
656    }
657}
658
659impl From<UnboundTypeVariableError> for ParseErrors {
660    fn from(err: UnboundTypeVariableError) -> Self {
661        ParseErrors::from(ParseError::from(err))
662    }
663}