Skip to content

Commit c30695f

Browse files
committed
Add HigherOrderFn infrastructure
Signed-off-by: Matt Katz <mhkatz97@gmail.com>
1 parent d8e744f commit c30695f

8 files changed

Lines changed: 627 additions & 0 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::any::type_name;
5+
use std::fmt::Debug;
6+
use std::fmt::Display;
7+
use std::fmt::Formatter;
8+
use std::hash::Hash;
9+
use std::hash::Hasher;
10+
use std::sync::Arc;
11+
12+
use vortex_error::VortexExpect;
13+
use vortex_error::VortexResult;
14+
use vortex_error::vortex_err;
15+
use vortex_utils::debug_with::DebugWith;
16+
17+
use crate::higher_order_fn::HigherOrderFnId;
18+
use crate::higher_order_fn::HigherOrderFnOptions;
19+
use crate::higher_order_fn::HigherOrderFnVTable;
20+
use crate::higher_order_fn::TypedHigherOrderFnInstance;
21+
use crate::higher_order_fn::typed::DynHigherOrderFn;
22+
use crate::scalar_fn::Arity;
23+
use crate::scalar_fn::ChildName;
24+
25+
/// A type-erased higher-order function, pairing a vtable with per-call options.
26+
#[derive(Clone)]
27+
pub struct HigherOrderFnRef(pub(super) Arc<dyn DynHigherOrderFn>);
28+
29+
impl HigherOrderFnRef {
30+
/// Bind `vtable` with its per-call `options`.
31+
pub fn new<V: HigherOrderFnVTable>(vtable: V, options: V::Options) -> Self {
32+
TypedHigherOrderFnInstance::new(vtable, options).erased()
33+
}
34+
35+
/// The function's global identifier.
36+
pub fn id(&self) -> HigherOrderFnId {
37+
self.0.id()
38+
}
39+
40+
/// Whether this function uses vtable `V`.
41+
pub fn is<V: HigherOrderFnVTable>(&self) -> bool {
42+
self.0.as_any().is::<TypedHigherOrderFnInstance<V>>()
43+
}
44+
45+
/// Return typed options when this function uses vtable `V`.
46+
pub fn as_opt<V: HigherOrderFnVTable>(&self) -> Option<&V::Options> {
47+
self.0
48+
.as_any()
49+
.downcast_ref::<TypedHigherOrderFnInstance<V>>()
50+
.map(TypedHigherOrderFnInstance::options)
51+
}
52+
53+
/// Return typed options for vtable `V`.
54+
///
55+
/// # Panics
56+
///
57+
/// Panics if this function does not use vtable `V`.
58+
pub fn as_<V: HigherOrderFnVTable>(&self) -> &V::Options {
59+
self.as_opt::<V>()
60+
.vortex_expect("higher-order function options type mismatch")
61+
}
62+
63+
/// Return these options behind an opaque type-erased handle.
64+
pub fn options(&self) -> HigherOrderFnOptions<'_> {
65+
HigherOrderFnOptions { inner: &*self.0 }
66+
}
67+
68+
/// Return the arity of the ordinary arguments.
69+
pub fn arity(&self) -> Arity {
70+
self.0.arity()
71+
}
72+
73+
/// Return the number of lambda arguments.
74+
pub fn lambda_arity(&self) -> usize {
75+
self.0.lambda_arity()
76+
}
77+
78+
/// Return the name of an ordinary argument.
79+
pub fn child_name(&self, child_idx: usize) -> ChildName {
80+
self.0.child_name(child_idx)
81+
}
82+
83+
/// Serialize the per-call options.
84+
pub fn serialize(&self) -> VortexResult<Option<Vec<u8>>> {
85+
self.0.options_serialize()
86+
}
87+
88+
/// Downcast this function to its typed representation.
89+
pub fn try_downcast<V: HigherOrderFnVTable>(
90+
self,
91+
) -> Result<Arc<TypedHigherOrderFnInstance<V>>, Self> {
92+
if self.is::<V>() {
93+
let ptr = Arc::into_raw(self.0) as *const TypedHigherOrderFnInstance<V>;
94+
Ok(unsafe { Arc::from_raw(ptr) })
95+
} else {
96+
Err(self)
97+
}
98+
}
99+
100+
/// Downcast this function to its typed representation.
101+
///
102+
/// # Panics
103+
///
104+
/// Panics if this function does not use vtable `V`.
105+
pub fn downcast<V: HigherOrderFnVTable>(self) -> Arc<TypedHigherOrderFnInstance<V>> {
106+
self.try_downcast::<V>()
107+
.map_err(|function| {
108+
vortex_err!(
109+
"failed to downcast higher-order function {} to {}",
110+
function.id(),
111+
type_name::<V>(),
112+
)
113+
})
114+
.vortex_expect("failed to downcast higher-order function")
115+
}
116+
}
117+
118+
impl Debug for HigherOrderFnRef {
119+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
120+
f.debug_struct("HigherOrderFnRef")
121+
.field("vtable", &self.id())
122+
.field("options", &DebugWith(|fmt| self.0.options_debug(fmt)))
123+
.finish()
124+
}
125+
}
126+
127+
impl Display for HigherOrderFnRef {
128+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
129+
write!(f, "{}(", self.id())?;
130+
self.0.options_display(f)?;
131+
write!(f, ")")
132+
}
133+
}
134+
135+
impl PartialEq for HigherOrderFnRef {
136+
fn eq(&self, other: &Self) -> bool {
137+
self.id() == other.id() && self.0.options_eq(other.0.options_any())
138+
}
139+
}
140+
141+
impl Eq for HigherOrderFnRef {}
142+
143+
impl Hash for HigherOrderFnRef {
144+
fn hash<H: Hasher>(&self, state: &mut H) {
145+
self.id().hash(state);
146+
self.0.options_hash(state);
147+
}
148+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
//! Higher-order function vtable machinery.
5+
//!
6+
//! Higher-order functions accept ordinary arguments and lambda arguments. This module provides
7+
//! their identity, type erasure, per-call options, and session registry.
8+
9+
use vortex_session::registry::Id;
10+
11+
mod erased;
12+
pub use erased::HigherOrderFnRef;
13+
14+
mod options;
15+
pub use options::HigherOrderFnOptions;
16+
17+
mod typed;
18+
pub use typed::TypedHigherOrderFnInstance;
19+
20+
mod plugin;
21+
pub use plugin::*;
22+
23+
pub mod session;
24+
25+
mod vtable;
26+
pub use vtable::*;
27+
28+
/// A globally unique identifier for a higher-order function.
29+
pub type HigherOrderFnId = Id;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::any::Any;
5+
use std::fmt::Debug;
6+
use std::fmt::Display;
7+
use std::hash::Hash;
8+
use std::hash::Hasher;
9+
10+
use vortex_error::VortexResult;
11+
12+
use crate::higher_order_fn::typed::DynHigherOrderFn;
13+
14+
/// An opaque handle to the options of a higher-order function.
15+
pub struct HigherOrderFnOptions<'a> {
16+
pub(super) inner: &'a dyn DynHigherOrderFn,
17+
}
18+
19+
impl Display for HigherOrderFnOptions<'_> {
20+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21+
self.inner.options_display(f)
22+
}
23+
}
24+
25+
impl Debug for HigherOrderFnOptions<'_> {
26+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27+
self.inner.options_debug(f)
28+
}
29+
}
30+
31+
impl PartialEq for HigherOrderFnOptions<'_> {
32+
fn eq(&self, other: &Self) -> bool {
33+
self.inner.id() == other.inner.id() && self.inner.options_eq(other.inner.options_any())
34+
}
35+
}
36+
37+
impl Eq for HigherOrderFnOptions<'_> {}
38+
39+
impl Hash for HigherOrderFnOptions<'_> {
40+
fn hash<H: Hasher>(&self, state: &mut H) {
41+
self.inner.id().hash(state);
42+
self.inner.options_hash(state);
43+
}
44+
}
45+
46+
impl HigherOrderFnOptions<'_> {
47+
/// Serialize these options to a byte vector.
48+
pub fn serialize(&self) -> VortexResult<Option<Vec<u8>>> {
49+
self.inner.options_serialize()
50+
}
51+
52+
/// Return the underlying typed options as [`Any`].
53+
pub fn as_any(&self) -> &dyn Any {
54+
self.inner.options_any()
55+
}
56+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::sync::Arc;
5+
6+
use vortex_error::VortexResult;
7+
use vortex_session::VortexSession;
8+
9+
use crate::higher_order_fn::HigherOrderFnId;
10+
use crate::higher_order_fn::HigherOrderFnRef;
11+
use crate::higher_order_fn::HigherOrderFnVTable;
12+
use crate::higher_order_fn::TypedHigherOrderFnInstance;
13+
14+
/// Reference-counted pointer to a higher-order function plugin.
15+
pub type HigherOrderFnPluginRef = Arc<dyn HigherOrderFnPlugin>;
16+
17+
/// Registry trait for ID-based deserialization of higher-order functions.
18+
pub trait HigherOrderFnPlugin: 'static + Send + Sync {
19+
/// Return the ID for this higher-order function.
20+
fn id(&self) -> HigherOrderFnId;
21+
22+
/// Deserialize a higher-order function from serialized metadata.
23+
fn deserialize(
24+
&self,
25+
metadata: &[u8],
26+
session: &VortexSession,
27+
) -> VortexResult<HigherOrderFnRef>;
28+
}
29+
30+
impl std::fmt::Debug for dyn HigherOrderFnPlugin {
31+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32+
f.debug_tuple("HigherOrderFnPlugin")
33+
.field(&self.id())
34+
.finish()
35+
}
36+
}
37+
38+
impl<V: HigherOrderFnVTable> HigherOrderFnPlugin for V {
39+
fn id(&self) -> HigherOrderFnId {
40+
V::id(self)
41+
}
42+
43+
fn deserialize(
44+
&self,
45+
metadata: &[u8],
46+
session: &VortexSession,
47+
) -> VortexResult<HigherOrderFnRef> {
48+
let options = HigherOrderFnVTable::deserialize(self, metadata, session)?;
49+
Ok(TypedHigherOrderFnInstance::new(self.clone(), options).erased())
50+
}
51+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use std::any::Any;
5+
use std::sync::Arc;
6+
7+
use vortex_session::ArcSwapMap;
8+
use vortex_session::SessionExt;
9+
use vortex_session::SessionGuard;
10+
use vortex_session::SessionVar;
11+
12+
use crate::higher_order_fn::HigherOrderFnId;
13+
use crate::higher_order_fn::HigherOrderFnPluginRef;
14+
use crate::higher_order_fn::HigherOrderFnVTable;
15+
16+
/// Registry of higher-order function vtables.
17+
pub type HigherOrderFnRegistry = ArcSwapMap<HigherOrderFnId, HigherOrderFnPluginRef>;
18+
19+
/// Session state for higher-order function vtables.
20+
#[derive(Clone, Debug, Default)]
21+
pub struct HigherOrderFnSession {
22+
registry: HigherOrderFnRegistry,
23+
}
24+
25+
impl HigherOrderFnSession {
26+
pub fn registry(&self) -> &HigherOrderFnRegistry {
27+
&self.registry
28+
}
29+
30+
/// Register a vtable, replacing any existing vtable with the same ID.
31+
pub fn register<V: HigherOrderFnVTable>(&self, vtable: V) {
32+
self.registry
33+
.insert(vtable.id(), Arc::new(vtable) as HigherOrderFnPluginRef);
34+
}
35+
}
36+
37+
impl SessionVar for HigherOrderFnSession {
38+
fn as_any(&self) -> &dyn Any {
39+
self
40+
}
41+
42+
fn as_any_mut(&mut self) -> &mut dyn Any {
43+
self
44+
}
45+
}
46+
47+
/// Extension trait for accessing higher-order-function session state.
48+
pub trait HigherOrderFnSessionExt: SessionExt {
49+
/// Return the higher-order function vtable registry.
50+
fn higher_order_fns(&self) -> SessionGuard<'_, HigherOrderFnSession> {
51+
self.get::<HigherOrderFnSession>()
52+
}
53+
}
54+
55+
impl<S: SessionExt> HigherOrderFnSessionExt for S {}

0 commit comments

Comments
 (0)