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}