Skip to content

Instantly share code, notes, and snippets.

@kkspeed
Created January 18, 2019 04:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kkspeed/a80cbb5336511ffd1992b0831c3c4681 to your computer and use it in GitHub Desktop.
Save kkspeed/a80cbb5336511ffd1992b0831c3c4681 to your computer and use it in GitHub Desktop.
A naive Rust Functional Reactive Programming Framework
use std::cell::RefCell;
use std::rc::Rc;
struct Core<T> {
observers: Vec<Box<Fn()>>,
value: Option<T>,
}
impl<T> Core<T>
where
T: Clone,
{
fn new() -> Self {
Core::<T> {
observers: vec![],
value: None,
}
}
fn add_observer(&mut self, f: Box<Fn()>) {
self.observers.push(f);
}
fn set_value(&mut self, v: T) {
self.value.replace(v);
}
fn has_value(&self) -> bool {
self.value.is_some()
}
fn value(&self) -> T {
self.value.clone().unwrap()
}
fn drop_value(&mut self) {
self.value.take();
}
fn notify(&self) {
if !self.has_value() {
return;
}
for f in self.observers.iter() {
f();
}
}
}
pub struct Signal<T> {
core: Rc<RefCell<Core<T>>>,
}
impl<T> Signal<T>
where
T: Sized + Clone + 'static,
{
pub fn new() -> Self {
Signal::<T> {
core: Rc::new(RefCell::new(Core::<T>::new())),
}
}
pub fn activate(&self, v: T) {
self.core.borrow_mut().set_value(v);
self.core.borrow().notify();
self.core.borrow_mut().drop_value();
}
pub fn sink(&self, f: Box<Fn(T)>) {
let this_core = self.core.clone();
self.core.borrow_mut().add_observer(Box::new(move || {
f(this_core.borrow().value());
}));
}
pub fn fmap<B: Sized + Clone + 'static>(&self, f: Box<Fn(T) -> B>) -> Signal<B> {
let new_signal = Signal::<B>::new();
let this_core = self.core.clone();
let new_core = new_signal.core.clone();
self.core.borrow_mut().add_observer(Box::new(move || {
new_core
.borrow_mut()
.set_value(f(this_core.borrow().value()));
new_core.borrow().notify();
new_core.borrow_mut().drop_value();
}));
new_signal
}
pub fn filter(&self, f: Box<Fn(T) -> bool>) -> Signal<T> {
let new_signal = Signal::<T>::new();
let new_core = new_signal.core.clone();
let this_core = self.core.clone();
self.core.borrow_mut().add_observer(Box::new(move || {
if !f(this_core.borrow().value()) {
new_core.borrow_mut().drop_value();
return;
}
new_core.borrow_mut().set_value(this_core.borrow().value());
new_core.borrow().notify();
new_core.borrow_mut().drop_value();
}));
new_signal
}
pub fn fold<A: Sized + Clone + 'static>(&self, init: A, f: Box<Fn(A, T) -> A>) -> Signal<A> {
let state: Rc<RefCell<Option<A>>> = Rc::new(RefCell::new(None));
let new_signal = Signal::<A>::new();
let new_core = new_signal.core.clone();
let this_core = self.core.clone();
self.core.borrow_mut().add_observer(Box::new(move || {
let v = init.clone();
if !state.borrow().is_some() {
state.borrow_mut().replace(v);
}
let next = f(state.borrow().clone().unwrap(), this_core.borrow().value());
state.borrow_mut().replace(next.clone());
new_core.borrow_mut().set_value(next);
new_core.borrow().notify();
new_core.borrow_mut().drop_value();
}));
new_signal
}
pub fn sync<B: Sized + Clone + 'static>(&self, other: &Signal<B>) -> Signal<(T, B)> {
let new_signal = Signal::<(T, B)>::new();
let new_core = new_signal.core.clone();
let this_core = self.core.clone();
let state: Rc<RefCell<(Option<T>, Option<B>)>> = Rc::new(RefCell::new((None, None)));
let state_shared = state.clone();
self.core.borrow_mut().add_observer(Box::new(move || {
let val = (
Some(this_core.borrow().value()),
(*state_shared.borrow()).1.clone(),
);
*state_shared.borrow_mut() = val.clone();
match val {
(Some(x), Some(y)) => {
new_core.borrow_mut().set_value((x, y));
new_core.borrow().notify();
new_core.borrow_mut().drop_value();
*state_shared.borrow_mut() = (None, None);
}
_ => (),
};
}));
let new_core = new_signal.core.clone();
let other_core = other.core.clone();
let state_shared = state.clone();
other.core.borrow_mut().add_observer(Box::new(move || {
let val = (
(*state_shared.borrow()).0.clone(),
Some(other_core.borrow().value()),
);
*state_shared.borrow_mut() = val.clone();
match val {
(Some(x), Some(y)) => {
new_core.borrow_mut().set_value((x, y));
new_core.borrow().notify();
new_core.borrow_mut().drop_value();
*state_shared.borrow_mut() = (None, None);
}
_ => (),
};
}));
new_signal
}
pub fn merge(&self, other: &Signal<T>) -> Signal<T> {
let new_signal = Signal::<T>::new();
let new_core = new_signal.core.clone();
let this_core = self.core.clone();
self.core.borrow_mut().add_observer(Box::new(move || {
new_core.borrow_mut().set_value(this_core.borrow().value());
new_core.borrow().notify();
new_core.borrow_mut().drop_value();
}));
let new_core = new_signal.core.clone();
let other_core = other.core.clone();
other.core.borrow_mut().add_observer(Box::new(move || {
new_core.borrow_mut().set_value(other_core.borrow().value());
new_core.borrow().notify();
new_core.borrow_mut().drop_value();
}));
new_signal
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fmap() {
let v = Rc::new(RefCell::new(0));
let s = Signal::<i32>::new();
let z = v.clone();
let _ = s
.fmap(Box::new(|x| x * 2))
.sink(Box::new(move |x| *z.borrow_mut() = x));
s.activate(10);
assert_eq!(*v.borrow(), 20);
s.activate(20);
assert_eq!(*v.borrow(), 40);
}
#[test]
fn test_filter_fold() {
let v = Rc::new(RefCell::new(-1));
let z = v.clone();
let s = Signal::<i32>::new();
let _ = s
.fmap(Box::new(|x| x * 2))
.filter(Box::new(|x| x % 3 == 0))
.fold(0, Box::new(|x, y| x + y))
.sink(Box::new(move |x| *z.borrow_mut() = x));
s.activate(0);
assert_eq!(*v.borrow(), 0);
s.activate(1);
assert_eq!(*v.borrow(), 0);
s.activate(2);
assert_eq!(*v.borrow(), 0);
s.activate(3);
assert_eq!(*v.borrow(), 6);
s.activate(4);
assert_eq!(*v.borrow(), 6);
s.activate(5);
assert_eq!(*v.borrow(), 6);
s.activate(6);
assert_eq!(*v.borrow(), 18);
}
#[test]
fn test_sync() {
let s1 = Signal::<i32>::new();
let s2 = Signal::<i32>::new();
let s3 = s1.sync(&s2);
let v: Rc<RefCell<Option<(i32, i32)>>> = Rc::new(RefCell::new(None));
let z = v.clone();
s3.sink(Box::new(move |x| *z.borrow_mut() = Some(x)));
s1.activate(3);
assert!(!v.borrow().is_some());
*v.borrow_mut() = None;
s2.activate(5);
assert_eq!(*v.borrow(), Some((3, 5)));
*v.borrow_mut() = None;
s2.activate(6);
assert!(!v.borrow().is_some());
}
#[test]
fn test_merge() {
let s1 = Signal::<i32>::new();
let s2 = Signal::<i32>::new();
let s3 = s1.merge(&s2);
let v = Rc::new(RefCell::new(0));
let z = v.clone();
s3.sink(Box::new(move |x| *z.borrow_mut() = x));
s1.activate(3);
assert_eq!(*v.borrow(), 3);
s2.activate(5);
assert_eq!(*v.borrow(), 5);
s1.activate(6);
assert_eq!(*v.borrow(), 6);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment