Skip to main content

nu_protocol/
ty_relation.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2pub enum TypeRelation {
3    /// Strict subtype
4    Subtype,
5    Equal,
6    /// Strict supertype
7    Supertype,
8}
9
10impl TypeRelation {
11    pub fn reverse(self) -> Self {
12        match self {
13            Self::Subtype => Self::Supertype,
14            Self::Equal => Self::Equal,
15            Self::Supertype => Self::Subtype,
16        }
17    }
18
19    /// Combines two partial relation results.
20    ///
21    /// While computing the relation of two types, sometimes it has to be done in multiple steps,
22    /// e.g. `oneof`, `record`.
23    /// -   In both cases, one may start with [`Self::Equal`] before processing any type parameters.
24    /// -   If both types are empty (e.g. `record<>` with no columns, `oneof<>` with no variants),
25    ///     [`Self::Equal`] is the correct result without requiring further checks.
26    /// -   As items are checked, the result of these checks may not form a coherent result.
27    ///     e.g.: `lhs = record<a: int, b: int>` and `rhs = record<a: int, c: int>`
28    ///     -   both `lhs` and `rhs` have an `a: int` column. `lhs` and `rhs` might be [Equal](Self::Equal)
29    ///     -   `lhs` has a `b: int` column, which `rhs` does not. This makes `lhs` the more
30    ///         specific type between the two, thus `lhs` might be a [Subtype](Self::Subtype) of `rhs`
31    ///     -   `lhs` does not have a `b: int` column, which `rhs` does. This makes `rhs` the more
32    ///         specific type between the two, thus `lhs` might be a [Supertype](Self::Supertype) of `rhs`
33    /// -   If there are conflicting results, returns [`None`]
34    pub fn combine(self, other: Self) -> Option<Self> {
35        match (self, other) {
36            (s, Self::Equal) | (Self::Equal, s) => Some(s),
37            (Self::Subtype, Self::Subtype) => Some(Self::Subtype),
38            (Self::Supertype, Self::Supertype) => Some(Self::Supertype),
39            _ => None,
40        }
41    }
42}
43
44/// Trait for comparisons corresponding to subtyping relations.
45///
46/// `<:` and `:>` are used to mean "is subtype of" and "is supertype of" respectively.
47/// `<<:` and `:>>` are used as their "strict" counterparts (no equality).
48///
49/// Implementations must be:
50/// - *Transitive*: if `a <: b` and `b <: c`, then `a <: c`
51/// - *Dual*: if `a <: b`, then `b :> a`
52/// - *Antisymmetric*:
53///   - if `a <<: b`, then `b <<: a` can't be true
54///   - if `a <: b` and `b <: a`, then `a == b`
55///
56/// Output of [`Self::is_subtype_of`] and [`Self::is_supertype_of`] must be consistent with
57/// [`Self::compare_types`]
58///
59/// The `Rhs` type parameter is to allow comparing the runtime type of a value with static types,
60/// without going through intermediate steps.
61/// Instead of `Value::get_type(&val).compare_types(&ty)`, which might require allocations for the
62/// intermediate `Type`, one can use `Value::compare_types(&val, &ty)`
63pub trait CompareTypes<Rhs = Self> {
64    /// Compares types and returns their relation with regards to subtyping.
65    ///
66    /// - Returns `Some` if types are equal, or one type is a subtype of the other.
67    /// - Returns `None` if neither type is equal to or is subtype of the other.
68    fn compare_types(&self, other: &Rhs) -> Option<TypeRelation>;
69
70    /// Returns `true` if `self` is equal to or is a *strict* subtype of `other`
71    ///
72    /// Converse of [`Self::is_supertype_of`]
73    fn is_subtype_of(&self, other: &Rhs) -> bool {
74        matches!(
75            self.compare_types(other),
76            Some(TypeRelation::Subtype | TypeRelation::Equal)
77        )
78    }
79
80    /// Returns `true` if `self` is equal to or is a *strict* supertype of `other`
81    ///
82    /// Converse of [`Self::is_subtype_of`]
83    fn is_supertype_of(&self, other: &Rhs) -> bool {
84        matches!(
85            self.compare_types(other),
86            Some(TypeRelation::Supertype | TypeRelation::Equal)
87        )
88    }
89
90    /// Allows having an "escape hatch".
91    ///
92    /// Due to not having separate top and bottom types, and treating `any` as both, we need to be
93    /// especially lax in some situations to keep things convenient and backwards compatible.
94    ///
95    /// We can't treat `any` as a bottom type in [`CompareTypes::compare_types`] as due to
96    /// reflexivity it would imply all types are subtypes of all other types:
97    /// - `any` as top type: `int <: any`
98    /// - `any` as *bottom* type: `any <: list`
99    /// - `int <: list`???
100    fn is_any(&self) -> bool {
101        false
102    }
103
104    /// Equivalent to [`CompareTypes::is_subtype_of`] by default.
105    ///
106    /// Exists as a separate method to allow relaxing requirements when needed.
107    fn is_assignable_to(&self, dst: &Rhs) -> bool {
108        self.is_subtype_of(dst)
109    }
110}
111
112/// Trait for set operations on types.
113pub trait TypeSet {
114    /// Returns the narrowest common supertype of the given types.
115    #[doc(alias = "widen")]
116    fn union(self, other: Self) -> Self;
117
118    // // no actual use case yet. this is just where it should be when/if it's implemented
119    // #[doc(alias = "narrow")]
120    // fn intersection(self, other: Self) -> Option<Self>;
121}