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}