Skip to content

Commit 37c71b9

Browse files
committed
Upgraded protocols so we can use instanceof, and gave us a macro to get
rid of the boilerplate
1 parent 54d6e40 commit 37c71b9

File tree

1 file changed

+131
-8
lines changed

1 file changed

+131
-8
lines changed

src/protocol.rs

Lines changed: 131 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
use crate::value::Value;
22
use std::rc::Rc;
33

4-
// @TODO should we just exclusively call them protocols as
5-
// we've been doing (deciding that Clojure without the host
6-
// has Protocols as its natural abstraction for interfaces)
7-
// or should interfaces, as an abstraction, exist (and perhaps
8-
// be used by Protocols even?)
94
/// A type that implements Protocol is one that has a pseudo downcasting of
105
///
116
/// value.as::<ISeq>()
@@ -16,26 +11,154 @@ use std::rc::Rc;
1611
/// functions to the Value inside, and unwrap() it to get your Value back when
1712
/// you're done
1813
pub trait Protocol: Sized {
19-
fn try_as_protocol(val: &Rc<Value>) -> Option<Self>; // where Self:Sized;
14+
fn instanceof(val: &Rc<Value>) -> bool;
15+
16+
// The following exist so we can build the rest of the trait functions on this generic behavior
17+
/// Simply wraps the value in this protocol
18+
/// whether its a valid cast or not
19+
fn raw_wrap(val: &Rc<Value>) -> Self;
20+
21+
/// Simply retrieves the Rc<Value> stored in the Protocol,
22+
/// whether its valid or not
23+
fn raw_unwrap(&self) -> Rc<Value>;
24+
25+
// @TODO consider renaming downcast
26+
fn try_as_protocol(val: &Rc<Value>) -> Option<Self> {
27+
if Self::instanceof(val) {
28+
Some(Self::raw_wrap(val))
29+
}
30+
else {
31+
None
32+
}
33+
}
34+
2035
/// Panics if your Value isn't an instance of Protocol
2136
fn as_protocol(val: &Rc<Value>) -> Self {
2237
Self::try_as_protocol(val).unwrap()
2338
}
24-
fn try_unwrap(&self) -> Option<Rc<Value>>;
25-
/// Panics if your Value isn't an instance of Protocol
39+
40+
fn try_unwrap(&self) -> Option<Rc<Value>>{
41+
let inner_value = self.raw_unwrap();
42+
if Self::instanceof(&inner_value) {
43+
Some(inner_value)
44+
}
45+
else {
46+
None
47+
}
48+
}
49+
// Realistically, the fact that you unwrap to get an upcast is just an implementation detail, so
50+
// @TODO change to upcast
51+
/// Panics if Value not instance of Protocol
2652
fn unwrap(&self) -> Rc<Value> {
2753
self.try_unwrap().unwrap()
2854
}
2955
}
3056
pub trait ProtocolCastable {
57+
fn instanceof<T: Protocol>(&self) -> bool;
3158
fn try_as_protocol<T: Protocol>(&self) -> Option<T>;
3259
fn as_protocol<T: Protocol>(&self) -> T;
3360
}
61+
3462
impl ProtocolCastable for Rc<Value> {
63+
fn instanceof<T: Protocol>(&self) -> bool {
64+
T::instanceof(self)
65+
}
3566
fn try_as_protocol<T: Protocol>(&self) -> Option<T> {
3667
T::try_as_protocol(self)
3768
}
3869
fn as_protocol<T: Protocol>(&self) -> T {
3970
T::as_protocol(self)
4071
}
4172
}
73+
74+
// @TODO Consider changing syntax to differentiate protocol from variants
75+
// @TODO Consider trade offs of having a very plain function like macro of macro!(a,b,c)
76+
// and having a clearer one like define_protocol(Iterable = A | B | C)
77+
#[macro_export]
78+
macro_rules! define_protocol {
79+
// define_protocol!(Protocol = A | B)
80+
($protocol:ident = $($variant:ident) |*) => {
81+
#[derive(Debug, Clone)]
82+
pub struct $protocol {
83+
value: Rc<Value>
84+
}
85+
impl crate::protocol::Protocol for $protocol {
86+
fn raw_wrap(val: &Rc<Value>) -> Self {
87+
$protocol { value: Rc::clone(val) }
88+
}
89+
fn raw_unwrap(&self) -> Rc<Value> {
90+
Rc::clone(&self.value)
91+
}
92+
fn instanceof(val: &Rc<Value>) -> bool {
93+
match &**val {
94+
$(
95+
crate::value::Value::$variant(_) => true,
96+
)*
97+
_ => false
98+
}
99+
}
100+
}
101+
};
102+
($protocol:ident, $($variant:ident), *) => {
103+
#[derive(Debug, Clone)]
104+
pub struct $protocol {
105+
value: Rc<Value>
106+
}
107+
impl crate::protocol::Protocol for $protocol {
108+
fn raw_wrap(val: &Rc<Value>) -> Self {
109+
$protocol { value: Rc::clone(val) }
110+
}
111+
fn raw_unwrap(&self) -> Rc<Value> {
112+
Rc::clone(&self.value)
113+
}
114+
fn instanceof(val: &Rc<Value>) -> bool {
115+
match &**val {
116+
$(
117+
crate::value::Value::$variant(_) => true,
118+
)*
119+
_ => false
120+
}
121+
}
122+
}
123+
};
124+
}
125+
// @TODO next trick; extend_protocol, so that it actually wraps trait equivalent of protocol
126+
// Ie, make it so that protocol::IFn is itself of trait IFn, and automatically
127+
// wraps its trait functions
128+
// @TODO Think, however, whether its worth it to always have a trait and protocol
129+
// I believe so -- I think basically the idea is the trait wraps true Rust values,
130+
// like symbol::Symbol, and the protocol wraps its ClojureRS equivalent, like Value::Symbol
131+
//
132+
//
133+
// traits and traits are both to give us interface behavior, traits give that to us
134+
// for our rust primitives like symbol::Symbol, but too much of the power of both is at compile time.
135+
// Just as we made the Value to give us a value whose rust type could be unknown at runtime,
136+
// we created the Protocol to give us an interface we could cast Values to and from at runtime,
137+
// as well as some other benefits we need
138+
//
139+
// macro_rules! extend_protocol {
140+
// ($protocol:ident, $fn_name:ident, $fn:expr, $($variant:ident), *) => {
141+
//
142+
// impl $protocol {
143+
// fn $fn_name(val: &Rc<Value>) -> Option<Self> {
144+
// match &**val {
145+
// $(
146+
// crate::value::Value::$variant(_) => Some($protocol {
147+
// value: Rc::clone(val),
148+
// }),
149+
// )*
150+
// _ => None
151+
// }
152+
// }
153+
// fn try_unwrap(&self) -> Option<Rc<Value>> {
154+
// match &*self.value {
155+
// $(
156+
// crate::value::Value::$variant(_) => Some(Rc::clone(&self.value)),
157+
// )*
158+
// _ => None
159+
// }
160+
// }
161+
// }
162+
// };
163+
// }
164+
//

0 commit comments

Comments
 (0)