polydat_core/iteration/comprehension/surfaces/instance.rs
1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! `KernelScope` trait + `ScopedKernelInstance` — the
5//! second-order surface's value type (spec §9.5).
6//!
7//! `KernelScope` is the algebra-layer abstraction over
8//! "a thing that can be scoped to a coordinate tuple."
9//! polydat-core's `PolydatKernelScope` (`surfaces/polydat_kernel.rs`)
10//! implements it over a `PolydatKernel` pair; tests can implement
11//! it with a lightweight mock so the surfaces can be exercised
12//! without pulling in the full Polydat runtime.
13
14use crate::iteration::comprehension::strategies::Tuple;
15
16/// A parent value that can be scoped to a coordinate tuple,
17/// producing an instance of `Self::Scoped`. Spec §9.5.3 names
18/// this the "one-shot scope function."
19///
20/// For polydat kernels this is
21/// `PolydatKernel::for_iteration(&canonical, &parent, &bindings)`.
22/// For tests it can be any type that derives a scoped value
23/// from a tuple.
24pub trait KernelScope {
25 /// The scoped value produced by `scope`.
26 type Scoped;
27
28 /// Apply `coords` to this parent and produce a scoped
29 /// instance. Pure function — same `(parent, coords)`
30 /// always produces equivalent output.
31 fn scope(&self, coords: &Tuple) -> Self::Scoped;
32}
33
34/// The result of scoping a parent kernel against a coord
35/// tuple. Wraps the scoped value (`Scoped`) and the
36/// originating coord tuple for traceability.
37#[derive(Debug, Clone, PartialEq)]
38pub struct ScopedKernelInstance<S> {
39 /// The coordinate tuple the kernel was scoped against.
40 pub coords: Tuple,
41 /// The scoped value.
42 pub scoped: S,
43}
44
45impl<S> ScopedKernelInstance<S> {
46 /// An instance of `scoped` at `coords`.
47 pub fn new(coords: Tuple, scoped: S) -> Self {
48 Self { coords, scoped }
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use crate::iteration::comprehension::strategies::TupleValue;
56
57 /// Test mock: a parent kernel that records its name and,
58 /// when scoped, returns a `MockScoped` carrying the parent
59 /// name + the coord tuple.
60 #[derive(Debug, Clone)]
61 struct MockKernel {
62 name: String,
63 }
64
65 #[derive(Debug, Clone, PartialEq)]
66 struct MockScoped {
67 parent_name: String,
68 coords: Tuple,
69 }
70
71 impl KernelScope for MockKernel {
72 type Scoped = MockScoped;
73 fn scope(&self, coords: &Tuple) -> MockScoped {
74 MockScoped {
75 parent_name: self.name.clone(),
76 coords: coords.clone(),
77 }
78 }
79 }
80
81 #[test]
82 fn mock_kernel_scope() {
83 let parent = MockKernel {
84 name: "phase_x".into(),
85 };
86 let coords = Tuple::new().with("k", TupleValue::I64(42));
87 let scoped = parent.scope(&coords);
88 assert_eq!(scoped.parent_name, "phase_x");
89 assert_eq!(scoped.coords.bindings[0].0, "k");
90 }
91
92 #[test]
93 fn scoped_instance_construction() {
94 let coords = Tuple::new().with("limit", TupleValue::I64(100));
95 let instance = ScopedKernelInstance::new(coords.clone(), "scoped_value".to_string());
96 assert_eq!(instance.coords, coords);
97 assert_eq!(instance.scoped, "scoped_value");
98 }
99}