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}