Skip to content

Instantly share code, notes, and snippets.

@freddi301
Created February 6, 2020 16:30
Show Gist options
  • Save freddi301/90f66617aebefb41f57667fea65ec11c to your computer and use it in GitHub Desktop.
Save freddi301/90f66617aebefb41f57667fea65ec11c to your computer and use it in GitHub Desktop.
Rust hooks
use std::assert;
use std::rc::Rc;
mod rust_hooks {
use std::cell::{Cell, RefCell};
use std::rc::Rc;
pub trait Notifiable {
fn notify(&self);
}
pub trait Subscribeable {
fn subscribe(&self, listener: Rc<dyn Notifiable>);
}
pub trait Valuable<Value> {
fn value(&self) -> Option<Value>;
}
#[macro_export]
macro_rules! use_state {
() => {{
use rust_hooks::*;
Rc::new(State::new())
}};
}
#[macro_export]
macro_rules! use_memo {
($body:expr, [$($dependency:ident),*]) => {
{
use rust_hooks::*;
let wired = Rc::new(Memo::new({
$(let $dependency = $dependency.clone();)*
move || {
if let ($(Some($dependency),)*) = ($($dependency.value(),)*) {
Some(dbg!($body))
} else {
None
}
}
}));
$($dependency.subscribe(wired.clone());)*
wired
}
};
}
#[macro_export]
macro_rules! use_effect {
($body:expr, [$($dependency:ident),*]) => {
{
use rust_hooks::*;
let wired = Rc::new(Effect::new({
$(let $dependency = $dependency.clone();)*
move || {
if let ($(Some($dependency),)*) = ($(($dependency).value(),)*) {
Some(dbg!($body))
} else {
None
}
}
}));
$($dependency.subscribe(wired.clone());)*
wired
}
};
}
#[macro_export]
macro_rules! use_scanner {
(|$state:pat| $body:expr, $initial:expr, [$($dependency:ident),*]) => {
{
use rust_hooks::*;
let wired = Rc::new(Scanner::new({
$(let $dependency = $dependency.clone();)*
move |$state| {
if let ($(Some($dependency),)*) = ($(($dependency).value(),)*) {
Some(dbg!($body))
} else {
None
}
}
}, $initial));
$($dependency.subscribe(wired.clone());)*
wired
}
};
}
struct Notifier {
listeners: Vec<Rc<dyn Notifiable>>,
}
impl Notifier {
fn new() -> Self {
Notifier {
listeners: Vec::new(),
}
}
fn subscribe(&mut self, listener: Rc<dyn Notifiable>) {
self.listeners.push(listener);
}
}
impl Notifiable for Notifier {
fn notify(&self) {
for listener in &self.listeners {
listener.notify();
}
}
}
pub struct State<Value> {
value: Cell<Option<Value>>,
notifier: RefCell<Notifier>,
}
impl<Value> State<Value> {
pub fn new() -> Self {
State {
value: Cell::new(None),
notifier: RefCell::new(Notifier::new()),
}
}
pub fn set(&self, value: Value) {
self.value.set(Some(value));
self.notifier.borrow().notify();
}
}
impl<Value> Subscribeable for State<Value> {
fn subscribe(&self, listener: Rc<dyn Notifiable>) {
self.notifier.borrow_mut().subscribe(listener);
}
}
impl<Value: Copy> Valuable<Value> for State<Value> {
fn value(&self) -> Option<Value> {
self.value.get()
}
}
pub struct Memo<F: Fn() -> Option<Value>, Value> {
closure: F,
value: Cell<Option<Value>>,
notifier: RefCell<Notifier>,
}
impl<F: Fn() -> Option<Value>, Value> Memo<F, Value> {
pub fn new(closure: F) -> Self {
Memo {
closure,
value: Cell::new(None),
notifier: RefCell::new(Notifier::new()),
}
}
}
impl<F: Fn() -> Option<Value>, Value> Notifiable for Memo<F, Value> {
fn notify(&self) {
let closure = &self.closure;
let value = closure();
self.value.set(value);
self.notifier.borrow().notify();
}
}
impl<F: Fn() -> Option<Value>, Value> Subscribeable for Memo<F, Value> {
fn subscribe(&self, listener: Rc<dyn Notifiable>) {
self.notifier.borrow_mut().subscribe(listener);
}
}
impl<F: Fn() -> Option<Value>, Value: Copy> Valuable<Value> for Memo<F, Value> {
fn value(&self) -> Option<Value> {
self.value.get()
}
}
pub struct Effect<F: Fn() -> Option<Value>, Value> {
closure: F,
value: Cell<Option<Value>>,
notifier: RefCell<Notifier>,
}
impl<F: Fn() -> Option<Value>, Value> Effect<F, Value> {
pub fn new(closure: F) -> Self {
Effect {
closure,
value: Cell::new(None),
notifier: RefCell::new(Notifier::new()),
}
}
}
impl<F: Fn() -> Option<Value>, Value> Notifiable for Effect<F, Value> {
fn notify(&self) {
let closure = &self.closure;
let value = closure();
self.value.set(value);
self.notifier.borrow().notify();
}
}
impl<F: Fn() -> Option<Value>, Value> Subscribeable for Effect<F, Value> {
fn subscribe(&self, listener: Rc<dyn Notifiable>) {
self.notifier.borrow_mut().subscribe(listener);
}
}
impl<F: Fn() -> Option<Value>, Value: Copy> Valuable<Value> for Effect<F, Value> {
fn value(&self) -> Option<Value> {
self.value.get()
}
}
pub struct Scanner<F: Fn(Value) -> Option<Value>, Value> {
closure: F,
value: Cell<Value>,
notifier: RefCell<Notifier>,
}
impl<F: Fn(Value) -> Option<Value>, Value> Scanner<F, Value> {
pub fn new(closure: F, initial: Value) -> Self {
Scanner {
closure,
value: Cell::new(initial),
notifier: RefCell::new(Notifier::new()),
}
}
}
impl<F: Fn(Value) -> Option<Value>, Value: Copy> Notifiable for Scanner<F, Value> {
fn notify(&self) {
let closure = &self.closure;
let value = closure(self.value.get());
if let Some(value) = value {
self.value.set(value);
self.notifier.borrow().notify();
}
}
}
impl<F: Fn(Value) -> Option<Value>, Value> Subscribeable for Scanner<F, Value> {
fn subscribe(&self, listener: Rc<dyn Notifiable>) {
self.notifier.borrow_mut().subscribe(listener);
}
}
impl<F: Fn(Value) -> Option<Value>, Value: Copy> Valuable<Value> for Scanner<F, Value> {
fn value(&self) -> Option<Value> {
Some(self.value.get())
}
}
}
fn main() {
use rust_hooks::Valuable;
#[derive(Debug, Clone, Copy)]
struct Dumb<T>(T);
let a = use_state!();
let b = use_memo!(a + 1, [a]);
let c = use_memo!(a * b, [a, b]);
use_effect!(dbg!(a), [a]);
use_effect!(dbg!(b), [b]);
use_effect!(dbg!(c), [c]);
let d = use_scanner!(|d| d + a, 0, [a]);
let e = use_scanner!(|Dumb(e)| Dumb(e + d), Dumb(0), [d]);
use_effect!(dbg!(d), [d]);
use_effect!(dbg!(e), [e]);
// use_effect!((a, b, c, d), [a, b, c, d]);
a.set(1);
assert!(b.value() == Some(2));
a.set(100);
}
/*
TODO
- useMemo should be lazy (do nothing if no subscribers)
- useEffect should be strict (always execute closure)
- useScanner should be lazy
- fix notifier notifying multiple times (remove dropped listeners)
*/
mod lazy {
use std::cell::UnsafeCell;
use std::mem::transmute;
use std::ops::Deref;
// TODO FnMut -> FnOnce
enum Status<Thunk: FnMut() -> Value, Value> {
Value(Value),
Thunk(Thunk),
}
pub struct Lazy<Thunk: FnMut() -> Value, Value> {
inner: UnsafeCell<Status<Thunk, Value>>,
}
impl<Thunk: FnMut() -> Value, Value> Lazy<Thunk, Value> {
pub fn new(producer: Thunk) -> Self {
Lazy {
inner: UnsafeCell::new(Status::Thunk(producer)),
}
}
}
impl<Thunk: FnMut() -> Value, Value> Deref for Lazy<Thunk, Value> {
type Target = Value;
fn deref(&self) -> &Self::Target {
let status: &mut Status<Thunk, Value> = unsafe { transmute(self.inner.get()) };
match status {
Status::Value(value) => value,
Status::Thunk(producer) => {
let value = producer();
*status = Status::Value(value);
self.deref()
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment