~raph/immutable

97045ac1d76a1958e9dd424e81cf5b9ab1741a36 — Raph Levien 2 years ago 0da2892
Switch to reference-counted subtrees
2 files changed, 23 insertions(+), 20 deletions(-)

M src/main.rs
M src/value.rs
M src/main.rs => src/main.rs +1 -1
@@ 74,7 74,7 @@ impl AppState {
        let path = vec![PathEl::Map("val".into())];
        let delta = vec![DeltaEl {
            path,
            new_value: Some(Value::Float(*event)),
            new_value: Some((*event).into()),
        }];
        if let Ok(new_data) = self.data.apply(&delta) {
            self.data = new_data.clone();

M src/value.rs => src/value.rs +22 -19
@@ 1,5 1,8 @@
//! The data model for application state.

use std::ops::Deref;
use std::sync::Arc;

use std::collections::HashMap;

/// This is a placeholder for a proper error type.


@@ 7,16 10,14 @@ pub type Error = ();

/// The basic data type manipulated by this library.
///
/// This is very similar to serde_json's `Value`, but we don't *necessarily* want
/// to depend on that, and perhaps want to extend this to handle binary data etc.
///
/// There are other optimization concerns that will likely cause further divergence
/// from json_serde, in particular we will probably have `Arc` for subtree sharing,
/// and perhaps immutable data structures for arrays and maps, to reduce the cost
/// of applying deltas and the memory burden of multiple instances with shared
/// subtrees. However, for prototyping purposes we will just use simple types.
/// This is similar to serde_json's `Value`, but with some important
/// differences. The main one is that it's reference counted for
/// low-cost sharing of subtrees.
#[derive(Clone, Debug)]
pub struct Value(Arc<ValueEnum>);

#[derive(Clone, Debug)]
pub enum Value {
pub enum ValueEnum {
    Float(f64),
    String(String),
    List(Vec<Value>),


@@ 33,6 34,8 @@ pub enum PathEl {
}

// TODO: we probably want &[PathEl] for most read-only cases.
// Actually there are a lot of tradeoffs, it could be ref-counted and even a linked list
// to make lifetimes / cloning easier.
pub type Path = Vec<PathEl>;

/// One element of a delta - replaces a subtree at a specific location.


@@ 51,14 54,14 @@ impl Value {
        for el in path {
            match el {
                PathEl::List(ix) => {
                    if let Value::List(l) = node {
                    if let ValueEnum::List(l) = node.0.deref() {
                        node = l.get(*ix)?;
                    } else {
                        return None;
                    }
                }
                PathEl::Map(key) => {
                    if let Value::Map(m) = node {
                    if let ValueEnum::Map(m) = node.0.deref() {
                        node = m.get(key)?;
                    } else {
                        return None;


@@ 86,7 89,7 @@ impl Value {
        if let Some(el) = path.first() {
            match el {
                PathEl::List(ix) => {
                    if let Value::List(l) = self {
                    if let ValueEnum::List(l) = self.0.deref() {
                        let mut l = l.clone();
                        if *ix < l.len() {
                            if let Some(new) = new_value {


@@ 105,13 108,13 @@ impl Value {
                        } else {
                            return Err(());
                        }
                        Ok(Value::List(l))
                        Ok(l.into())
                    } else {
                        Err(())
                    }
                }
                PathEl::Map(key) => {
                    if let Value::Map(m) = self {
                    if let ValueEnum::Map(m) = self.0.deref() {
                        let mut m = m.clone();
                        if let Some(new) = new_value {
                            m.insert(key.clone(), new.clone());


@@ 120,7 123,7 @@ impl Value {
                            // case more consistent with the array case.
                            m.remove(key);
                        }
                        Ok(Value::Map(m))
                        Ok(m.into())
                    } else {
                        Err(())
                    }


@@ 134,24 137,24 @@ impl Value {

impl From<String> for Value {
    fn from(s: String) -> Value {
        Value::String(s)
        Value(Arc::new(ValueEnum::String(s)))
    }
}

impl From<f64> for Value {
    fn from(f: f64) -> Value {
        Value::Float(f)
        Value(Arc::new(ValueEnum::Float(f)))
    }
}

impl From<Vec<Value>> for Value {
    fn from(v: Vec<Value>) -> Value {
        Value::List(v)
        Value(Arc::new(ValueEnum::List(v)))
    }
}

impl From<HashMap<String, Value>> for Value {
    fn from(m: HashMap<String, Value>) -> Value {
        Value::Map(m)
        Value(Arc::new(ValueEnum::Map(m)))
    }
}