Skip to main content

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}