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