Skip to main content

reify_reflect_core/
lib.rs

1#![deny(unsafe_code)]
2
3//! Core traits and types for type-level reification and reflection in Rust.
4//!
5//! This is the foundation crate. It defines:
6//!
7//! - [`Reflect`]: a trait for types that carry a compile-time value, with
8//!   [`reflect()`](Reflect::reflect) extracting that value at runtime.
9//! - [`reify`]: a function that takes any runtime value and lifts it into
10//!   a scoped type-level context, where it is available through a
11//!   [`Reified`] token branded with an invariant lifetime that cannot
12//!   escape the callback.
13//! - [`RuntimeValue`]: a small, structural enum used as the standard
14//!   payload type for [`Reflect`] implementations across the workspace.
15//!
16//! Everything is fully safe (`#![deny(unsafe_code)]`), with scoping
17//! enforced by the borrow checker.
18//!
19//! # When should I use this crate?
20//!
21//! - You're writing a library where types encode values (Peano numbers,
22//!   type-level flags, dimensional units, etc.) and want a uniform way to
23//!   surface those values at runtime: implement [`Reflect`] for them.
24//! - You want a Haskell-`reflection`-style scoping primitive in Rust: use
25//!   [`reify`] to thread a runtime value into a callback as if it were a
26//!   compile-time fact.
27//! - You're writing a downstream crate (such as
28//!   [`reflect-nat`](https://docs.rs/reflect-nat) or
29//!   [`reflect-derive`](https://docs.rs/reflect-derive)) that produces
30//!   [`Reflect`] implementations: depend on this crate for the trait and
31//!   the [`RuntimeValue`] vocabulary.
32//!
33//! # The reification / reflection pattern
34//!
35//! This implements the pattern from Kiselyov & Shan's *Functional Pearl:
36//! Implicit Configurations*, popularized by Kmett's Haskell `reflection`
37//! library, adapted to Rust with branded lifetimes for scoping safety.
38//!
39//! In Haskell:
40//!
41//! ```haskell
42//! reify   :: a -> (forall s. Reifies s a => Proxy s -> r) -> r
43//! reflect :: Reifies s a => proxy s -> a
44//! ```
45//!
46//! In Rust, the `forall s` is modeled by an invariant lifetime `'brand`
47//! on [`Reified`]. The higher-rank bound `for<'brand>` on the callback
48//! ensures the token cannot escape, just as Haskell's rank-2 type
49//! prevents `s` from escaping.
50//!
51//! Unlike Haskell's `reflection` library (which uses `unsafeCoerce` to
52//! fabricate typeclass dictionaries from GHC internals), this
53//! implementation is safe all the way down: no unsafe code, no
54//! compiler-internal assumptions. Scoping is enforced mechanically by
55//! the borrow checker.
56//!
57//! # Examples
58//!
59//! Lift a runtime value into a scoped type-level context:
60//!
61//! ```
62//! use reify_reflect_core::reify;
63//!
64//! let result = reify(&42i32, |token| {
65//!     let val: &i32 = token.reflect();
66//!     *val + 1
67//! });
68//! assert_eq!(result, 43);
69//! ```
70//!
71//! Implement [`Reflect`] for a type that carries a compile-time value:
72//!
73//! ```
74//! use reify_reflect_core::{Reflect, RuntimeValue};
75//!
76//! struct Pi;
77//!
78//! impl Reflect for Pi {
79//!     type Value = RuntimeValue;
80//!     fn reflect() -> Self::Value {
81//!         RuntimeValue::Nat(3)  // close enough
82//!     }
83//! }
84//!
85//! assert_eq!(Pi::reflect(), RuntimeValue::Nat(3));
86//! ```
87//!
88//! See also: [`reflect-nat`](https://docs.rs/reflect-nat) for ready-made
89//! [`Reflect`] implementations on Peano naturals, booleans, and HLists,
90//! and [`reflect-derive`](https://docs.rs/reflect-derive) for
91//! `#[derive(Reflect)]` on user types.
92
93use std::marker::PhantomData;
94
95/// Converts a type-level value into a runtime value.
96///
97/// Types implementing `Reflect` carry a compile-time value that can be
98/// extracted at runtime via [`Reflect::reflect`].
99///
100/// # Examples
101///
102/// ```
103/// use reify_reflect_core::{Reflect, RuntimeValue};
104///
105/// struct MyZero;
106///
107/// impl Reflect for MyZero {
108///     type Value = RuntimeValue;
109///     fn reflect() -> Self::Value {
110///         RuntimeValue::Nat(0)
111///     }
112/// }
113///
114/// assert_eq!(MyZero::reflect(), RuntimeValue::Nat(0));
115/// ```
116pub trait Reflect {
117    /// The runtime representation of this type-level value.
118    type Value;
119
120    /// Produce the runtime value corresponding to this type-level value.
121    fn reflect() -> Self::Value;
122}
123
124/// A branded token carrying a reified value.
125///
126/// The lifetime `'brand` is existential, created fresh by each call to
127/// [`reify`] and prevented from escaping the callback by the higher-rank
128/// bound `for<'brand>`. This mirrors Haskell's
129/// `forall s. Reifies s a => Proxy s -> r` scoping.
130///
131/// The [`PhantomData`] carrying `fn(&'brand ()) -> &'brand ()`
132/// makes `'brand` *invariant*, preventing the compiler from shrinking
133/// or growing it to unify with any other lifetime. This is what makes
134/// the brand unique.
135///
136/// # Examples
137///
138/// ```
139/// use reify_reflect_core::reify;
140///
141/// reify(&"hello", |token| {
142///     assert_eq!(*token.reflect(), "hello");
143/// });
144/// ```
145///
146/// The token cannot escape:
147///
148/// ```compile_fail
149/// use reify_reflect_core::reify;
150///
151/// let escaped = reify(&42, |token| {
152///     token // ERROR: borrowed data escapes the closure
153/// });
154/// ```
155pub struct Reified<'brand, T: ?Sized> {
156    value: &'brand T,
157    // Invariant in 'brand: prevents lifetime coercion
158    _brand: PhantomData<fn(&'brand ()) -> &'brand ()>,
159}
160
161impl<'brand, T: ?Sized> Reified<'brand, T> {
162    /// Reflect the reified value back to a runtime reference.
163    ///
164    /// This is the Rust equivalent of Haskell's
165    /// `reflect :: Reifies s a => proxy s -> a`.
166    ///
167    /// The branded lifetime ensures this reference cannot outlive the
168    /// [`reify`] callback that created this token.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// use reify_reflect_core::reify;
174    ///
175    /// let doubled = reify(&21i32, |token| {
176    ///     token.reflect() * 2
177    /// });
178    /// assert_eq!(doubled, 42);
179    /// ```
180    pub fn reflect(&self) -> &T {
181        self.value
182    }
183}
184
185/// Reify a runtime value into a scoped type-level context.
186///
187/// This is the Rust equivalent of Haskell's
188/// `reify :: a -> (forall s. Reifies s a => Proxy s -> r) -> r`.
189///
190/// The callback receives a [`Reified`] token branded with a fresh lifetime.
191/// The `for<'brand>` bound ensures this lifetime cannot escape the closure,
192/// so the token (and any references derived from it) are confined to the
193/// callback's scope.
194///
195/// **No unsafe code.** Unlike Haskell's `reflection` library (which uses
196/// `unsafeCoerce` to fabricate typeclass dictionaries), this implementation
197/// relies only on Rust's borrow checker for scoping safety.
198///
199/// # Examples
200///
201/// Basic reification and reflection:
202///
203/// ```
204/// use reify_reflect_core::reify;
205///
206/// let result = reify(&100u64, |token| {
207///     token.reflect() + 1
208/// });
209/// assert_eq!(result, 101);
210/// ```
211///
212/// Works with any type:
213///
214/// ```
215/// use reify_reflect_core::reify;
216///
217/// let result = reify(&vec![1, 2, 3], |token| {
218///     token.reflect().iter().sum::<i32>()
219/// });
220/// assert_eq!(result, 6);
221/// ```
222///
223/// Nested reification:
224///
225/// ```
226/// use reify_reflect_core::reify;
227///
228/// let result = reify(&10i32, |outer| {
229///     reify(&20i32, |inner| {
230///         outer.reflect() + inner.reflect()
231///     })
232/// });
233/// assert_eq!(result, 30);
234/// ```
235///
236/// Composing with [`Reflect`]:
237///
238/// ```
239/// use reify_reflect_core::{reify, Reflect, RuntimeValue};
240///
241/// struct Three;
242/// impl Reflect for Three {
243///     type Value = RuntimeValue;
244///     fn reflect() -> RuntimeValue { RuntimeValue::Nat(3) }
245/// }
246///
247/// let result = reify(&Three::reflect(), |token| {
248///     match token.reflect() {
249///         RuntimeValue::Nat(n) => *n * 2,
250///         _ => panic!("expected Nat"),
251///     }
252/// });
253/// assert_eq!(result, 6);
254/// ```
255pub fn reify<T: ?Sized, F, R>(val: &T, f: F) -> R
256where
257    F: for<'brand> FnOnce(Reified<'brand, T>) -> R,
258{
259    // The lifetime of `val` is erased into the fresh `'brand`.
260    // The `for<'brand>` bound on F ensures no `Reified<'brand, T>`
261    // can escape — the caller must be parametric in 'brand.
262    f(Reified {
263        value: val,
264        _brand: PhantomData,
265    })
266}
267
268/// Runtime representation of type-level values.
269///
270/// This enum provides a uniform way to inspect type-level values at runtime,
271/// regardless of their original type-level encoding.
272///
273/// # Examples
274///
275/// ```
276/// use reify_reflect_core::RuntimeValue;
277///
278/// let nat = RuntimeValue::Nat(42);
279/// let boolean = RuntimeValue::Bool(true);
280/// let list = RuntimeValue::List(vec![
281///     RuntimeValue::Nat(1),
282///     RuntimeValue::Nat(2),
283/// ]);
284/// let unit = RuntimeValue::Unit;
285///
286/// assert_eq!(nat, RuntimeValue::Nat(42));
287/// ```
288#[derive(Debug, Clone, PartialEq)]
289pub enum RuntimeValue {
290    /// A natural number.
291    Nat(u64),
292    /// A boolean.
293    Bool(bool),
294    /// A list of runtime values.
295    List(Vec<RuntimeValue>),
296    /// The unit value.
297    Unit,
298}
299
300#[cfg(test)]
301mod tests {
302    use super::*;
303
304    #[test]
305    fn runtime_value_nat() {
306        let val = RuntimeValue::Nat(42);
307        assert_eq!(val, RuntimeValue::Nat(42));
308    }
309
310    #[test]
311    fn runtime_value_bool() {
312        let val = RuntimeValue::Bool(true);
313        assert_eq!(val, RuntimeValue::Bool(true));
314    }
315
316    #[test]
317    fn runtime_value_list() {
318        let val = RuntimeValue::List(vec![RuntimeValue::Nat(1), RuntimeValue::Nat(2)]);
319        assert_eq!(
320            val,
321            RuntimeValue::List(vec![RuntimeValue::Nat(1), RuntimeValue::Nat(2)])
322        );
323    }
324
325    #[test]
326    fn runtime_value_unit() {
327        assert_eq!(RuntimeValue::Unit, RuntimeValue::Unit);
328    }
329
330    #[test]
331    fn runtime_value_clone() {
332        let val = RuntimeValue::List(vec![RuntimeValue::Bool(false)]);
333        let cloned = val.clone();
334        assert_eq!(val, cloned);
335    }
336
337    struct TestZero;
338
339    impl Reflect for TestZero {
340        type Value = RuntimeValue;
341        fn reflect() -> Self::Value {
342            RuntimeValue::Nat(0)
343        }
344    }
345
346    #[test]
347    fn reflect_trait_works() {
348        assert_eq!(TestZero::reflect(), RuntimeValue::Nat(0));
349    }
350
351    #[test]
352    fn reify_basic() {
353        let result = reify(&42i32, |token| *token.reflect() + 1);
354        assert_eq!(result, 43);
355    }
356
357    #[test]
358    fn reify_string() {
359        let s = String::from("hello");
360        let len = reify(&s, |token| token.reflect().len());
361        assert_eq!(len, 5);
362    }
363
364    #[test]
365    fn reify_nested() {
366        let result = reify(&10i32, |outer| {
367            reify(&20i32, |inner| outer.reflect() + inner.reflect())
368        });
369        assert_eq!(result, 30);
370    }
371
372    #[test]
373    fn reify_vec() {
374        let data = vec![1, 2, 3, 4, 5];
375        let sum = reify(&data, |token| token.reflect().iter().sum::<i32>());
376        assert_eq!(sum, 15);
377    }
378
379    #[test]
380    fn reify_with_reflect_trait() {
381        let reflected = TestZero::reflect();
382        let result = reify(&reflected, |token| match token.reflect() {
383            RuntimeValue::Nat(n) => *n,
384            _ => panic!("expected Nat"),
385        });
386        assert_eq!(result, 0);
387    }
388
389    #[test]
390    fn reify_dyn_trait() {
391        // Works with unsized types via &dyn
392        trait Greet {
393            fn greet(&self) -> &str;
394        }
395        struct Hello;
396        impl Greet for Hello {
397            fn greet(&self) -> &str {
398                "hi"
399            }
400        }
401
402        let obj: &dyn Greet = &Hello;
403        let result = reify(obj, |token| token.reflect().greet().to_string());
404        assert_eq!(result, "hi");
405    }
406
407    #[test]
408    fn reify_slice() {
409        let data = [1, 2, 3];
410        let sum = reify(&data[..], |token| token.reflect().iter().sum::<i32>());
411        assert_eq!(sum, 6);
412    }
413}