// Example 207: Prism Laws — ReviewPreview and PreviewReview
struct Prism<S, A> {
preview: Box<dyn Fn(&S) -> Option<A>>,
review: Box<dyn Fn(A) -> S>,
}
impl<S: 'static, A: 'static> Prism<S, A> {
fn new(
preview: impl Fn(&S) -> Option<A> + 'static,
review: impl Fn(A) -> S + 'static,
) -> Self {
Prism { preview: Box::new(preview), review: Box::new(review) }
}
}
// Approach 1: Lawful prisms for a JSON type
#[derive(Debug, Clone, PartialEq)]
enum Json {
JString(String),
JInt(i64),
JBool(bool),
JNull,
JArray(Vec<Json>),
}
fn jstring_prism() -> Prism<Json, String> {
Prism::new(
|j| match j { Json::JString(s) => Some(s.clone()), _ => None },
|s| Json::JString(s),
)
}
fn jint_prism() -> Prism<Json, i64> {
Prism::new(
|j| match j { Json::JInt(n) => Some(*n), _ => None },
|n| Json::JInt(n),
)
}
fn jbool_prism() -> Prism<Json, bool> {
Prism::new(
|j| match j { Json::JBool(b) => Some(*b), _ => None },
|b| Json::JBool(b),
)
}
// Approach 2: An UNLAWFUL prism
fn bad_prism() -> Prism<Json, String> {
Prism::new(
|j| match j { Json::JString(s) => Some(s.to_uppercase()), _ => None },
|s| Json::JString(s), // review doesn't uppercase!
)
}
// Approach 3: Law verification
fn check_review_preview<S: PartialEq, A: Clone + PartialEq>(
prism: &Prism<S, A>, a: &A,
) -> bool {
let s = (prism.review)(a.clone());
(prism.preview)(&s) == Some(a.clone())
}
fn check_preview_review<S: PartialEq + Clone, A: Clone>(
prism: &Prism<S, A>, s: &S,
) -> bool {
match (prism.preview)(s) {
None => true,
Some(a) => (prism.review)(a) == *s,
}
}
fn main() {
// jstring_prism is lawful
let p = jstring_prism();
for val in &["hello".to_string(), "world".into(), "".into()] {
assert!(check_review_preview(&p, val));
}
for src in &[Json::JString("hello".into()), Json::JInt(42), Json::JNull] {
assert!(check_preview_review(&p, src));
}
// jint_prism is lawful
let p = jint_prism();
for val in &[1i64, 0, -99] {
assert!(check_review_preview(&p, val));
}
// bad_prism violates ReviewPreview
let bp = bad_prism();
assert!(!check_review_preview(&bp, &"hello".to_string()));
// preview(review("hello")) = Some("HELLO") ≠ Some("hello")
// Also violates PreviewReview
let s = Json::JString("hello".into());
assert!(!check_preview_review(&bp, &s));
// preview gives "HELLO", review("HELLO") = JString("HELLO") ≠ JString("hello")
println!("✓ All tests passed");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_review_preview_law() {
let p = jbool_prism();
assert!(check_review_preview(&p, &true));
assert!(check_review_preview(&p, &false));
}
#[test]
fn test_preview_review_law() {
let p = jint_prism();
assert!(check_preview_review(&p, &Json::JInt(42)));
assert!(check_preview_review(&p, &Json::JString("x".into())));
}
#[test]
fn test_bad_prism_violates() {
let bp = bad_prism();
assert!(!check_review_preview(&bp, &"test".to_string()));
}
}
(* Example 207: Prism Laws — ReviewPreview and PreviewReview *)
type ('s, 'a) prism = {
preview : 's -> 'a option;
review : 'a -> 's;
}
(* Prism Laws:
1. ReviewPreview: preview (review a) = Some a
(round-trip: inject then extract always succeeds)
2. PreviewReview: if preview s = Some a then review a = s
(round-trip: if extraction succeeds, re-injection gives back original)
*)
(* Approach 1: Lawful prisms *)
type json =
| JString of string
| JInt of int
| JBool of bool
| JNull
| JArray of json list
let jstring_prism : (json, string) prism = {
preview = (function JString s -> Some s | _ -> None);
review = (fun s -> JString s);
}
let jint_prism : (json, int) prism = {
preview = (function JInt n -> Some n | _ -> None);
review = (fun n -> JInt n);
}
let jbool_prism : (json, bool) prism = {
preview = (function JBool b -> Some b | _ -> None);
review = (fun b -> JBool b);
}
(* Approach 2: An UNLAWFUL prism *)
let bad_prism : (json, string) prism = {
preview = (function JString s -> Some (String.uppercase_ascii s) | _ -> None);
(* violates PreviewReview: preview gives uppercase, review uses original *)
review = (fun s -> JString s);
}
(* Approach 3: Law verification *)
let check_review_preview prism a =
prism.preview (prism.review a) = Some a
let check_preview_review prism s =
match prism.preview s with
| None -> true (* law only applies when preview succeeds *)
| Some a -> prism.review a = s
let verify_prism_laws name prism values sources =
let rp = List.for_all (check_review_preview prism) values in
let pr = List.for_all (check_preview_review prism) sources in
Printf.printf "%s: ReviewPreview=%b PreviewReview=%b\n" name rp pr;
(rp, pr)
(* === Tests === *)
let () =
(* jstring_prism is lawful *)
let (rp, pr) = verify_prism_laws "jstring"
jstring_prism
["hello"; "world"; ""]
[JString "hello"; JInt 42; JNull] in
assert rp; assert pr;
(* jint_prism is lawful *)
let (rp, pr) = verify_prism_laws "jint"
jint_prism
[1; 0; -99]
[JInt 1; JString "x"; JBool true] in
assert rp; assert pr;
(* bad_prism violates PreviewReview *)
let rp = check_review_preview bad_prism "hello" in
assert (not rp); (* preview(review "hello") = Some "HELLO" ≠ Some "hello" *)
(* Verify specific violation *)
let s = JString "hello" in
(match bad_prism.preview s with
| Some a -> assert (bad_prism.review a <> s) (* "HELLO" ≠ "hello" *)
| None -> assert false);
print_endline "✓ All tests passed"