Skip to main content

simplicity/node/
hiding.rs

1use crate::jet::Jet;
2use crate::node::{CoreConstructible, DisconnectConstructible, WitnessConstructible};
3use crate::types::{Arrow, Context, Error};
4use crate::{Cmr, FailEntropy, HasCmr, Word};
5
6#[derive(Clone, Debug)]
7enum HidingInner<'brand, N> {
8    Node(N),
9    Hidden { cmr: Cmr, arrow: Arrow<'brand> },
10}
11
12/// Wrapper that allows a node to be "hidden" during program construction.
13///
14/// ## Program construction
15///
16/// When a program is constructed in post-order,
17/// the parent node is created based on its children.
18/// We use this fact to introduce special branching logic:
19///
20/// 1. A `case` node with a left "hidden" child and a right non-hidden child becomes `assertr`.
21/// 2. A `case` node with a left non-hidden child and a right "hidden" child becomes `assertl`.
22/// 3. Otherwise, any node with "hidden" children becomes itself "hidden" with an updated CMR.
23/// 4. Any node with non-hidden children remains unchanged.
24///
25/// The program can be extracted from the wrapper when construction is finished.
26/// The program is invalid if the root node is "hidden".
27///
28/// ## Wrapping
29///
30/// A node can be wrapped via [`Hiding::from`] to add hiding support.
31/// A wrapped node can be converted into a "hidden" node via [`Hiding::hide`].
32/// Finally, a "hidden" node can be manually created via [`Hiding::hidden`].
33///
34/// ## Virtual hidden nodes
35///
36/// The wrapper merely _simulates_ hidden nodes.
37/// At no point are actual hidden nodes created.
38/// To stress this fact, I write "hidden" in quotation marks.
39#[derive(Clone, Debug)]
40pub struct Hiding<'brand, N> {
41    inner: HidingInner<'brand, N>,
42}
43
44impl<'brand, N> Hiding<'brand, N> {
45    /// Create a "hidden" node with the given CMR.
46    ///
47    /// To enable the construction of possible parent nodes,
48    /// the inference context of the current program must be passed.
49    pub fn hidden(cmr: Cmr, ctx: &Context<'brand>) -> Self {
50        Self {
51            inner: HidingInner::Hidden {
52                cmr,
53                arrow: Arrow::hidden(ctx),
54            },
55        }
56    }
57
58    /// If the node is not hidden, apply a function to the underlying node. If it is hidden,
59    /// apply a function to its CMR.
60    #[inline]
61    pub fn map_ref<M>(
62        &self,
63        mapfn: impl FnOnce(&N) -> M,
64        cmrfn: impl FnOnce(Cmr) -> Cmr,
65    ) -> Hiding<'brand, M> {
66        use core::convert::Infallible;
67        match self.map_ref_result::<_, Infallible>(|node| Ok(mapfn(node)), cmrfn) {
68            Ok(res) => res,
69            Err(inf) => match inf {},
70        }
71    }
72
73    /// If the node is not hidden, apply a function to the underlying node. If it is hidden,
74    /// do nothing.
75    #[inline]
76    pub fn map_ref_result<M, Err>(
77        &self,
78        mapfn: impl FnOnce(&N) -> Result<M, Err>,
79        cmrfn: impl FnOnce(Cmr) -> Cmr,
80    ) -> Result<Hiding<'brand, M>, Err> {
81        Ok(Hiding {
82            inner: match self.inner {
83                HidingInner::Node(ref n) => HidingInner::Node(mapfn(n)?),
84                HidingInner::Hidden { cmr, ref arrow } => HidingInner::Hidden {
85                    cmr: cmrfn(cmr),
86                    arrow: Arrow::hidden(arrow.inference_context()),
87                },
88            },
89        })
90    }
91
92    /// Access the non-hidden node inside in the wrapper.
93    ///
94    /// Return `None` if the wrapped node is "hidden".
95    pub fn as_node(&self) -> Option<&N> {
96        match self.inner {
97            HidingInner::Node(ref n) => Some(n),
98            HidingInner::Hidden { .. } => None,
99        }
100    }
101
102    /// Consume the wrapper and return the non-hidden node that was inside.
103    ///
104    /// Return `None` if the wrapped node is "hidden".
105    pub fn into_node(self) -> Option<N> {
106        match self.inner {
107            HidingInner::Node(n) => Some(n),
108            HidingInner::Hidden { .. } => None,
109        }
110    }
111}
112
113impl<'brand, N: HasCmr + CoreConstructible<'brand>> Hiding<'brand, N> {
114    /// If neither node is hidden, apply a function to the underlying nodes to produce a new
115    /// non-hidden node. If either node is hidden, apply an alternate function to the CMRs
116    /// to produce a new CMR.
117    ///
118    /// Non-public since the API is kinda messy.
119    fn zip_ref<M: CoreConstructible<'brand>>(
120        &self,
121        other: &Self,
122        node_zipfn: impl FnOnce(&N, &N) -> Result<M, Error>,
123        cmr_zipfn: impl FnOnce(Cmr, Cmr) -> Cmr,
124    ) -> Result<Hiding<'brand, M>, Error> {
125        Ok(Hiding {
126            inner: match (&self.inner, &other.inner) {
127                (HidingInner::Node(ref left), HidingInner::Node(ref right)) => {
128                    node_zipfn(left, right).map(HidingInner::Node)?
129                }
130                _ => {
131                    self.inference_context()
132                        .check_eq(other.inference_context())?;
133                    HidingInner::Hidden {
134                        cmr: cmr_zipfn(self.cmr(), other.cmr()),
135                        arrow: Arrow::hidden(self.inference_context()),
136                    }
137                }
138            },
139        })
140    }
141
142    /// Replace the node, if any, with its CMR; replace its type arrow with a new free arrow.
143    ///
144    /// Once hidden, a node's original type arrow loses its original bounds. In effect, a
145    /// hidden node is a completely separate node from its "original" node and is typechecked
146    /// and shared independently.
147    pub fn hide(self) -> Self {
148        match self.inner {
149            HidingInner::Node(node) => Self {
150                inner: HidingInner::Hidden {
151                    cmr: node.cmr(),
152                    arrow: Arrow::hidden(node.inference_context()),
153                },
154            },
155            HidingInner::Hidden { .. } => self,
156        }
157    }
158}
159
160impl<N: HasCmr> HasCmr for Hiding<'_, N> {
161    fn cmr(&self) -> Cmr {
162        match self.inner {
163            HidingInner::Node(ref node) => node.cmr(),
164            HidingInner::Hidden { cmr, .. } => cmr,
165        }
166    }
167}
168
169impl<'brand, N> From<N> for Hiding<'brand, N> {
170    fn from(node: N) -> Self {
171        Self {
172            inner: HidingInner::Node(node),
173        }
174    }
175}
176
177// # Soundness
178// See [`Hiding::hide`].
179impl<'brand, N: HasCmr + CoreConstructible<'brand>> CoreConstructible<'brand>
180    for Hiding<'brand, N>
181{
182    fn iden(inference_context: &Context<'brand>) -> Self {
183        N::iden(inference_context).into()
184    }
185
186    fn unit(inference_context: &Context<'brand>) -> Self {
187        N::unit(inference_context).into()
188    }
189
190    fn injl(child: &Self) -> Self {
191        child.map_ref(N::injl, Cmr::injl)
192    }
193
194    fn injr(child: &Self) -> Self {
195        child.map_ref(N::injr, Cmr::injr)
196    }
197
198    fn take(child: &Self) -> Self {
199        child.map_ref(N::take, Cmr::take)
200    }
201
202    fn drop_(child: &Self) -> Self {
203        child.map_ref(N::drop_, Cmr::drop)
204    }
205
206    fn comp(left: &Self, right: &Self) -> Result<Self, Error> {
207        left.zip_ref(right, N::comp, Cmr::comp)
208    }
209
210    fn case(left: &Self, right: &Self) -> Result<Self, Error> {
211        use HidingInner as I;
212
213        left.inference_context()
214            .check_eq(right.inference_context())?;
215        let inner = match (&left.inner, &right.inner) {
216            (I::Node(left), I::Node(right)) => I::Node(N::case(left, right)?),
217            (I::Hidden { cmr, .. }, I::Node(right)) => I::Node(N::assertr(*cmr, right)?),
218            (I::Node(left), I::Hidden { cmr, .. }) => I::Node(N::assertl(left, *cmr)?),
219            (I::Hidden { cmr: l_cmr, .. }, I::Hidden { cmr: r_cmr, .. }) => I::Hidden {
220                cmr: Cmr::case(*l_cmr, *r_cmr),
221                arrow: Arrow::hidden(left.inference_context()),
222            },
223        };
224
225        Ok(Self { inner })
226    }
227
228    fn assertl(left: &Self, right: Cmr) -> Result<Self, Error> {
229        left.map_ref_result(
230            |left| N::assertl(left, right),
231            |lcmr| Cmr::case(lcmr, right),
232        )
233    }
234
235    fn assertr(left: Cmr, right: &Self) -> Result<Self, Error> {
236        right.map_ref_result(
237            |right| N::assertr(left, right),
238            |rcmr| Cmr::case(left, rcmr),
239        )
240    }
241
242    fn pair(left: &Self, right: &Self) -> Result<Self, Error> {
243        left.zip_ref(right, N::pair, Cmr::pair)
244    }
245
246    fn fail(inference_context: &Context<'brand>, entropy: FailEntropy) -> Self {
247        N::fail(inference_context, entropy).into()
248    }
249
250    fn const_word(inference_context: &Context<'brand>, word: Word) -> Self {
251        N::const_word(inference_context, word).into()
252    }
253
254    fn jet(inference_context: &Context<'brand>, jet: &dyn Jet) -> Self {
255        N::jet(inference_context, jet).into()
256    }
257
258    fn arrow(&self) -> &Arrow<'brand> {
259        match self.inner {
260            HidingInner::Node(ref node) => node.arrow(),
261            HidingInner::Hidden { ref arrow, .. } => arrow,
262        }
263    }
264}
265
266impl<'brand, X, N> DisconnectConstructible<'brand, Option<X>> for Hiding<'brand, N>
267where
268    N: DisconnectConstructible<'brand, Option<X>> + CoreConstructible<'brand> + HasCmr,
269{
270    fn disconnect(left: &Self, right: &Option<X>) -> Result<Self, Error> {
271        left.map_ref_result(|left| N::disconnect(left, right), Cmr::disconnect)
272    }
273}
274
275impl<'brand, W, N> WitnessConstructible<'brand, W> for Hiding<'brand, N>
276where
277    N: WitnessConstructible<'brand, W> + CoreConstructible<'brand>,
278{
279    fn witness(inference_context: &Context<'brand>, witness: W) -> Self {
280        N::witness(inference_context, witness).into()
281    }
282}