-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor!: remove Value::Tuple
#1255
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -101,6 +101,76 @@ impl AsRef<Value> for Const { | |||||
} | ||||||
} | ||||||
|
||||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] | ||||||
struct SerialSum { | ||||||
#[serde(default)] | ||||||
tag: usize, | ||||||
#[serde(rename = "vs")] | ||||||
values: Vec<Value>, | ||||||
#[serde(default, rename = "typ")] | ||||||
sum_type: Option<SumType>, | ||||||
} | ||||||
|
||||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] | ||||||
#[serde(try_from = "SerialSum")] | ||||||
#[serde(into = "SerialSum")] | ||||||
/// A Sum variant, with a tag indicating the index of the variant and its | ||||||
/// value. | ||||||
pub struct Sum { | ||||||
/// The tag index of the variant. | ||||||
pub tag: usize, | ||||||
/// The value of the variant. | ||||||
/// | ||||||
/// Sum variants are always a row of values, hence the Vec. | ||||||
pub values: Vec<Value>, | ||||||
/// The full type of the Sum, including the other variants. | ||||||
pub sum_type: SumType, | ||||||
} | ||||||
|
||||||
impl Sum { | ||||||
/// If value is a sum with a single row variant, return the row. | ||||||
pub fn as_tuple(&self) -> Option<&[Value]> { | ||||||
self.sum_type.as_tuple().map(|_| self.values.as_ref()) | ||||||
} | ||||||
} | ||||||
|
||||||
impl TryFrom<SerialSum> for Sum { | ||||||
type Error = &'static str; | ||||||
|
||||||
fn try_from(value: SerialSum) -> Result<Self, Self::Error> { | ||||||
let SerialSum { | ||||||
tag, | ||||||
values, | ||||||
sum_type, | ||||||
} = value; | ||||||
|
||||||
let sum_type = if let Some(sum_type) = sum_type { | ||||||
sum_type | ||||||
} else { | ||||||
if tag != 0 { | ||||||
return Err("Sum type must be provided if tag is not 0"); | ||||||
} | ||||||
SumType::new_tuple(values.iter().map(Value::get_type).collect_vec()) | ||||||
}; | ||||||
|
||||||
Ok(Self { | ||||||
tag, | ||||||
values, | ||||||
sum_type, | ||||||
}) | ||||||
} | ||||||
} | ||||||
|
||||||
impl From<Sum> for SerialSum { | ||||||
fn from(value: Sum) -> Self { | ||||||
Self { | ||||||
tag: value.tag, | ||||||
values: value.values, | ||||||
sum_type: Some(value.sum_type), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is schema breaking - Sums are not allowed to have null (we won't be generating serialised "Tuple" from the rust any longer, I think to do that we would need to implement I'm happy with this compromise, but to really keep the "serialised tuple" optimisation I think we would want to rethink the serialised schema There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, that is annoying. |
||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] | ||||||
#[serde(tag = "v")] | ||||||
/// A value that can be stored as a static constant. Representing core types and | ||||||
|
@@ -118,25 +188,10 @@ pub enum Value { | |||||
/// A Hugr defining the function. | ||||||
hugr: Box<Hugr>, | ||||||
}, | ||||||
/// A tuple | ||||||
Tuple { | ||||||
/// Constant values in the tuple. | ||||||
vs: Vec<Value>, | ||||||
}, | ||||||
/// A Sum variant, with a tag indicating the index of the variant and its | ||||||
/// value. | ||||||
Sum { | ||||||
/// The tag index of the variant. | ||||||
tag: usize, | ||||||
/// The value of the variant. | ||||||
/// | ||||||
/// Sum variants are always a row of values, hence the Vec. | ||||||
#[serde(rename = "vs")] | ||||||
values: Vec<Value>, | ||||||
/// The full type of the Sum, including the other variants. | ||||||
#[serde(rename = "typ")] | ||||||
sum_type: SumType, | ||||||
}, | ||||||
#[serde(alias = "Tuple")] | ||||||
Sum(Sum), | ||||||
} | ||||||
|
||||||
/// An opaque newtype around a [`Box<dyn CustomConst>`](CustomConst). | ||||||
|
@@ -286,8 +341,7 @@ impl Value { | |||||
pub fn get_type(&self) -> Type { | ||||||
match self { | ||||||
Self::Extension { e } => e.get_type(), | ||||||
Self::Tuple { vs } => Type::new_tuple(vs.iter().map(Self::get_type).collect_vec()), | ||||||
Self::Sum { sum_type, .. } => sum_type.clone().into(), | ||||||
Self::Sum(Sum { sum_type, .. }) => sum_type.clone().into(), | ||||||
Self::Function { hugr } => { | ||||||
let func_type = mono_fn_type(hugr).unwrap_or_else(|e| panic!("{}", e)); | ||||||
Type::new_function(func_type) | ||||||
|
@@ -305,18 +359,19 @@ impl Value { | |||||
) -> Result<Self, ConstTypeError> { | ||||||
let values: Vec<Value> = items.into_iter().collect(); | ||||||
typ.check_type(tag, &values)?; | ||||||
Ok(Self::Sum { | ||||||
Ok(Self::Sum(Sum { | ||||||
tag, | ||||||
values, | ||||||
sum_type: typ, | ||||||
}) | ||||||
})) | ||||||
} | ||||||
|
||||||
/// Returns a tuple constant of constant values. | ||||||
pub fn tuple(items: impl IntoIterator<Item = Value>) -> Self { | ||||||
Self::Tuple { | ||||||
vs: items.into_iter().collect(), | ||||||
} | ||||||
let vs = items.into_iter().collect_vec(); | ||||||
let tys = vs.iter().map(Self::get_type).collect_vec(); | ||||||
|
||||||
Self::sum(0, vs, SumType::new_tuple(tys)).expect("Tuple type is valid") | ||||||
} | ||||||
|
||||||
/// Returns a constant function defined by a Hugr. | ||||||
|
@@ -334,7 +389,11 @@ impl Value { | |||||
|
||||||
/// Returns a constant unit type (empty Tuple). | ||||||
pub const fn unit() -> Self { | ||||||
Self::Tuple { vs: vec![] } | ||||||
Self::Sum(Sum { | ||||||
tag: 0, | ||||||
values: vec![], | ||||||
sum_type: SumType::Unit { size: 1 }, | ||||||
}) | ||||||
} | ||||||
|
||||||
/// Returns a constant Sum over units. Used as branching values. | ||||||
|
@@ -393,12 +452,17 @@ impl Value { | |||||
}; | ||||||
format!("const:function:[{}]", t) | ||||||
} | ||||||
Self::Tuple { vs: vals } => { | ||||||
let names: Vec<_> = vals.iter().map(Value::name).collect(); | ||||||
format!("const:seq:{{{}}}", names.iter().join(", ")) | ||||||
} | ||||||
Self::Sum { tag, values, .. } => { | ||||||
format!("const:sum:{{tag:{tag}, vals:{values:?}}}") | ||||||
Self::Sum(Sum { | ||||||
tag, | ||||||
values, | ||||||
sum_type, | ||||||
}) => { | ||||||
if sum_type.as_tuple().is_some() { | ||||||
let names: Vec<_> = values.iter().map(Value::name).collect(); | ||||||
format!("const:seq:{{{}}}", names.iter().join(", ")) | ||||||
} else { | ||||||
format!("const:sum:{{tag:{tag}, vals:{values:?}}}") | ||||||
} | ||||||
} | ||||||
} | ||||||
.into() | ||||||
|
@@ -409,8 +473,7 @@ impl Value { | |||||
match self { | ||||||
Self::Extension { e } => e.extension_reqs().clone(), | ||||||
Self::Function { .. } => ExtensionSet::new(), // no extensions required to load Hugr (only to run) | ||||||
Self::Tuple { vs } => ExtensionSet::union_over(vs.iter().map(Value::extension_reqs)), | ||||||
Self::Sum { values, .. } => { | ||||||
Self::Sum(Sum { values, .. }) => { | ||||||
ExtensionSet::union_over(values.iter().map(|x| x.extension_reqs())) | ||||||
} | ||||||
} | ||||||
|
@@ -424,22 +487,25 @@ impl Value { | |||||
mono_fn_type(hugr)?; | ||||||
Ok(()) | ||||||
} | ||||||
Self::Tuple { vs } => { | ||||||
for v in vs { | ||||||
v.validate()?; | ||||||
} | ||||||
Ok(()) | ||||||
} | ||||||
Self::Sum { | ||||||
Self::Sum(Sum { | ||||||
tag, | ||||||
values, | ||||||
sum_type, | ||||||
} => { | ||||||
}) => { | ||||||
sum_type.check_type(*tag, values)?; | ||||||
Ok(()) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
/// If value is a sum with a single row variant, return the row. | ||||||
pub fn as_tuple(&self) -> Option<&[Value]> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you should add this method to |
||||||
if let Self::Sum(sum) = self { | ||||||
sum.as_tuple() | ||||||
} else { | ||||||
None | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
impl<T> From<T> for Value | ||||||
|
@@ -662,7 +728,7 @@ mod test { | |||||
} | ||||||
|
||||||
mod proptest { | ||||||
use super::super::OpaqueValue; | ||||||
use super::super::{OpaqueValue, Sum}; | ||||||
use crate::{ | ||||||
ops::{constant::CustomSerialized, Value}, | ||||||
std_extensions::arithmetic::int_types::ConstInt, | ||||||
|
@@ -715,19 +781,19 @@ mod test { | |||||
3, // Each collection is up to 3 elements long | ||||||
|element| { | ||||||
prop_oneof![ | ||||||
vec(element.clone(), 0..3).prop_map(|vs| Self::Tuple { vs }), | ||||||
vec(element.clone(), 0..3).prop_map(Self::tuple), | ||||||
( | ||||||
any::<usize>(), | ||||||
vec(element.clone(), 0..3), | ||||||
any_with::<SumType>(1.into()) // for speed: don't generate large sum types for now | ||||||
) | ||||||
.prop_map( | ||||||
|(tag, values, sum_type)| { | ||||||
Self::Sum { | ||||||
Self::Sum(Sum { | ||||||
tag, | ||||||
values, | ||||||
sum_type, | ||||||
} | ||||||
}) | ||||||
} | ||||||
), | ||||||
] | ||||||
|
@@ -737,4 +803,75 @@ mod test { | |||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
#[test] | ||||||
fn test_tuple_deserialize() { | ||||||
let json = r#" | ||||||
{ | ||||||
"v": "Tuple", | ||||||
"vs": [ | ||||||
{ | ||||||
"v": "Sum", | ||||||
"tag": 0, | ||||||
"typ": { | ||||||
"t": "Sum", | ||||||
"s": "Unit", | ||||||
"size": 1 | ||||||
}, | ||||||
"vs": [] | ||||||
}, | ||||||
{ | ||||||
"v": "Sum", | ||||||
"tag": 1, | ||||||
"typ": { | ||||||
"t": "Sum", | ||||||
"s": "General", | ||||||
"rows": [ | ||||||
[ | ||||||
{ | ||||||
"t": "Sum", | ||||||
"s": "Unit", | ||||||
"size": 1 | ||||||
} | ||||||
], | ||||||
[ | ||||||
{ | ||||||
"t": "Sum", | ||||||
"s": "Unit", | ||||||
"size": 2 | ||||||
} | ||||||
] | ||||||
] | ||||||
}, | ||||||
"vs": [ | ||||||
{ | ||||||
"v": "Sum", | ||||||
"tag": 1, | ||||||
"typ": { | ||||||
"t": "Sum", | ||||||
"s": "Unit", | ||||||
"size": 2 | ||||||
}, | ||||||
"vs": [] | ||||||
} | ||||||
] | ||||||
} | ||||||
] | ||||||
} | ||||||
"#; | ||||||
|
||||||
let v: Value = serde_json::from_str(json).unwrap(); | ||||||
assert_eq!( | ||||||
v, | ||||||
Value::tuple([ | ||||||
Value::unit(), | ||||||
Value::sum( | ||||||
1, | ||||||
[Value::true_val()], | ||||||
SumType::new([vec![Type::UNIT], vec![Value::true_val().get_type()]]), | ||||||
) | ||||||
.unwrap() | ||||||
]) | ||||||
); | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could omit this when it is empty.
Value::unary_unit_sum
would then omit all fields! Relevant for values of any unary sum type. Perhaps this would be better as a future improvement.