git.delta.rocks / jrsonnet / refs/commits / 23d571a0df03

difftreelog

refactor move arrays to use dyn ArrayLike

Yaroslav Bolyukin2023-08-06parent: #09dae32.patch.diff
in: master

8 files changed

modifiedcrates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/arr/mod.rs
1use std::rc::Rc;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IBytes;5use jrsonnet_parser::LocExpr;67use crate::{function::FuncVal, Context, Result, Thunk, Val};89mod spec;10use spec::*;1112/// Represents a Jsonnet array value.13#[derive(Debug, Clone, Trace)]14// may contrain other ArrValue15#[trace(tracking(force))]16pub enum ArrValue {17	/// Layout optimized byte array.18	Bytes(BytesArray),19	/// Layout optimized char array.20	Chars(CharArray),21	/// Every element is lazy evaluated.22	Lazy(LazyArray),23	/// Every element is defined somewhere in source code24	Expr(ExprArray),25	/// Every field is already evaluated.26	Eager(EagerArray),27	/// Concatenation of two arrays of any kind.28	Extended(Cc<ExtendedArray>),29	/// Represents a integer array in form `[start, start + 1, ... end - 1, end]`.30	/// This kind of arrays is generated by `std.range(start, end)` call, and used for loops.31	Range(RangeArray),32	/// Sliced array view.33	Slice(Cc<SliceArray>),34	/// Reversed array view.35	/// Returned by `std.reverse(other)` call36	Reverse(Cc<ReverseArray>),37	/// Returned by `std.map` call38	Mapped(MappedArray),39	/// Returned by `std.repeat` call40	Repeated(RepeatedArray),41}4243pub trait ArrayLikeIter<T>: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator {}44impl<I, T> ArrayLikeIter<T> for I where45	I: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator46{47}4849impl ArrValue {50	pub fn empty() -> Self {51		Self::Range(RangeArray::empty())52	}5354	pub fn expr(ctx: Context, exprs: impl IntoIterator<Item = LocExpr>) -> Self {55		Self::Expr(ExprArray::new(ctx, exprs))56	}5758	pub fn lazy(thunks: Cc<Vec<Thunk<Val>>>) -> Self {59		Self::Lazy(LazyArray(thunks))60	}6162	pub fn eager(values: Vec<Val>) -> Self {63		Self::Eager(EagerArray(Cc::new(values)))64	}6566	pub fn repeated(data: ArrValue, repeats: usize) -> Option<Self> {67		Some(Self::Repeated(RepeatedArray::new(data, repeats)?))68	}6970	pub fn bytes(bytes: IBytes) -> Self {71		Self::Bytes(BytesArray(bytes))72	}73	pub fn chars(chars: impl Iterator<Item = char>) -> Self {74		Self::Chars(CharArray(Rc::new(chars.collect())))75	}7677	#[must_use]78	pub fn map(self, mapper: FuncVal) -> Self {79		Self::Mapped(MappedArray::new(self, mapper))80	}8182	pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {83		// TODO: ArrValue::Picked(inner, indexes) for large arrays84		let mut out = Vec::new();85		for i in self.iter() {86			let i = i?;87			if filter(&i)? {88				out.push(i);89			};90		}91		Ok(Self::eager(out))92	}9394	pub fn extended(a: ArrValue, b: ArrValue) -> Self {95		// TODO: benchmark for an optimal value, currently just a arbitrary choice96		const ARR_EXTEND_THRESHOLD: usize = 100;9798		if a.is_empty() {99			b100		} else if b.is_empty() {101			a102		} else if a.len() + b.len() > ARR_EXTEND_THRESHOLD {103			Self::Extended(Cc::new(ExtendedArray::new(a, b)))104		} else if let (Some(a), Some(b)) = (a.iter_cheap(), b.iter_cheap()) {105			let mut out = Vec::with_capacity(a.len() + b.len());106			out.extend(a);107			out.extend(b);108			Self::eager(out)109		} else {110			let mut out = Vec::with_capacity(a.len() + b.len());111			out.extend(a.iter_lazy());112			out.extend(b.iter_lazy());113			Self::lazy(Cc::new(out))114		}115	}116117	pub fn range_exclusive(a: i32, b: i32) -> Self {118		Self::Range(RangeArray::new_exclusive(a, b))119	}120	pub fn range_inclusive(a: i32, b: i32) -> Self {121		Self::Range(RangeArray::new_inclusive(a, b))122	}123124	#[must_use]125	pub fn slice(126		self,127		from: Option<usize>,128		to: Option<usize>,129		step: Option<usize>,130	) -> Option<Self> {131		let len = self.len();132		let from = from.unwrap_or(0);133		let to = to.unwrap_or(len).min(len);134		let step = step.unwrap_or(1);135136		if from >= to || step == 0 {137			return None;138		}139		// match self {140		// 	ArrValue::Slice(slice) => {141		// 		return Some(Self::Slice(Cc::new(SliceArray {142		// 			inner: slice.inner.clone(),143		// 			from: slice.from + slice.step * (from as u32),144		// 			to: slice.from + (to as u32) * slice.step,145		// 			step: slice.step * step as u32,146		// 		})))147		// 	}148		// 	_ => {}149		// }150151		Some(Self::Slice(Cc::new(SliceArray {152			inner: self,153			from: from as u32,154			to: to as u32,155			step: step as u32,156		})))157	}158159	/// Array length.160	pub fn len(&self) -> usize {161		pass!(self.len())162	}163164	/// Is array contains no elements?165	pub fn is_empty(&self) -> bool {166		pass!(self.is_empty())167	}168169	/// Get array element by index, evaluating it, if it is lazy.170	///171	/// Returns `None` on out-of-bounds condition.172	pub fn get(&self, index: usize) -> Result<Option<Val>> {173		pass!(self.get(index))174	}175176	/// Returns None if get is either non cheap, or out of bounds177	fn get_cheap(&self, index: usize) -> Option<Val> {178		pass!(self.get_cheap(index))179	}180181	/// Get array element by index, without evaluation.182	///183	/// Returns `None` on out-of-bounds condition.184	pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {185		pass!(self.get_lazy(index))186	}187188	pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {189		(0..self.len()).map(|i| self.get(i).transpose().expect("length checked"))190	}191192	/// Iterate over elements, returning lazy values.193	pub fn iter_lazy(&self) -> impl ArrayLikeIter<Thunk<Val>> + '_ {194		(0..self.len()).map(|i| self.get_lazy(i).expect("length checked"))195	}196197	pub fn iter_cheap(&self) -> Option<impl ArrayLikeIter<Val> + '_> {198		if self.is_cheap() {199			Some((0..self.len()).map(|i| self.get_cheap(i).expect("length and is_cheap checked")))200		} else {201			None202		}203	}204205	/// Return a reversed view on current array.206	#[must_use]207	pub fn reversed(self) -> Self {208		Self::Reverse(Cc::new(ReverseArray(self)))209	}210211	pub fn ptr_eq(a: &Self, b: &Self) -> bool {212		match (a, b) {213			(ArrValue::Bytes(a), ArrValue::Bytes(b)) => a.0 == b.0,214			(ArrValue::Lazy(a), ArrValue::Lazy(b)) => Cc::ptr_eq(&a.0, &b.0),215			(ArrValue::Expr(a), ArrValue::Expr(b)) => Cc::ptr_eq(&a.0, &b.0),216			(ArrValue::Eager(a), ArrValue::Eager(b)) => Cc::ptr_eq(&a.0, &b.0),217			(ArrValue::Extended(a), ArrValue::Extended(b)) => Cc::ptr_eq(a, b),218			(ArrValue::Range(a), ArrValue::Range(b)) => a == b,219			_ => false,220		}221	}222223	/// Is this vec supports `.get_cheap()?`224	pub fn is_cheap(&self) -> bool {225		match self {226			ArrValue::Eager(_) | ArrValue::Range(..) | ArrValue::Bytes(_) | ArrValue::Chars(_) => {227				true228			}229			ArrValue::Extended(v) => v.a.is_cheap() && v.b.is_cheap(),230			ArrValue::Slice(r) => r.inner.is_cheap(),231			ArrValue::Reverse(i) => i.0.is_cheap(),232			ArrValue::Repeated(v) => v.is_cheap(),233			ArrValue::Expr(_) | ArrValue::Lazy(_) | ArrValue::Mapped(_) => false,234		}235	}236}237impl From<Vec<Val>> for ArrValue {238	fn from(value: Vec<Val>) -> Self {239		Self::eager(value)240	}241}242impl From<Vec<Thunk<Val>>> for ArrValue {243	fn from(value: Vec<Thunk<Val>>) -> Self {244		Self::lazy(Cc::new(value))245	}246}247impl FromIterator<Val> for ArrValue {248	fn from_iter<T: IntoIterator<Item = Val>>(iter: T) -> Self {249		Self::eager(iter.into_iter().collect())250	}251}252253#[cfg(target_pointer_width = "64")]254static_assertions::assert_eq_size!(ArrValue, [u8; 16]);
after · crates/jrsonnet-evaluator/src/arr/mod.rs
1use std::any::Any;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IBytes;5use jrsonnet_parser::LocExpr;67use crate::{function::FuncVal, gc::TraceBox, tb, Context, Result, Thunk, Val};89mod spec;10pub use spec::ArrayLike;11pub(crate) use spec::*;1213/// Represents a Jsonnet array value.14#[derive(Debug, Clone, Trace)]15// may contrain other ArrValue16#[trace(tracking(force))]17pub struct ArrValue(Cc<TraceBox<dyn ArrayLike>>);1819pub trait ArrayLikeIter<T>: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator {}20impl<I, T> ArrayLikeIter<T> for I where21	I: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator22{23}2425impl ArrValue {26	pub fn new(v: impl ArrayLike) -> Self {27		Self(Cc::new(tb!(v)))28	}29	pub fn empty() -> Self {30		Self::new(RangeArray::empty())31	}3233	pub fn expr(ctx: Context, exprs: impl IntoIterator<Item = LocExpr>) -> Self {34		Self::new(ExprArray::new(ctx, exprs))35	}3637	pub fn lazy(thunks: Vec<Thunk<Val>>) -> Self {38		Self::new(LazyArray(thunks))39	}4041	pub fn eager(values: Vec<Val>) -> Self {42		Self::new(EagerArray(values))43	}4445	pub fn repeated(data: ArrValue, repeats: usize) -> Option<Self> {46		Some(Self::new(RepeatedArray::new(data, repeats)?))47	}4849	pub fn bytes(bytes: IBytes) -> Self {50		Self::new(BytesArray(bytes))51	}52	pub fn chars(chars: impl Iterator<Item = char>) -> Self {53		Self::new(CharArray(chars.collect()))54	}5556	#[must_use]57	pub fn map(self, mapper: FuncVal) -> Self {58		Self::new(MappedArray::new(self, mapper))59	}6061	pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {62		// TODO: ArrValue::Picked(inner, indexes) for large arrays63		let mut out = Vec::new();64		for i in self.iter() {65			let i = i?;66			if filter(&i)? {67				out.push(i);68			};69		}70		Ok(Self::eager(out))71	}7273	pub fn extended(a: ArrValue, b: ArrValue) -> Self {74		// TODO: benchmark for an optimal value, currently just a arbitrary choice75		const ARR_EXTEND_THRESHOLD: usize = 100;7677		if a.is_empty() {78			b79		} else if b.is_empty() {80			a81		} else if a.len() + b.len() > ARR_EXTEND_THRESHOLD {82			Self::new(ExtendedArray::new(a, b))83		} else if let (Some(a), Some(b)) = (a.iter_cheap(), b.iter_cheap()) {84			let mut out = Vec::with_capacity(a.len() + b.len());85			out.extend(a);86			out.extend(b);87			Self::eager(out)88		} else {89			let mut out = Vec::with_capacity(a.len() + b.len());90			out.extend(a.iter_lazy());91			out.extend(b.iter_lazy());92			Self::lazy(out)93		}94	}9596	pub fn range_exclusive(a: i32, b: i32) -> Self {97		Self::new(RangeArray::new_exclusive(a, b))98	}99	pub fn range_inclusive(a: i32, b: i32) -> Self {100		Self::new(RangeArray::new_inclusive(a, b))101	}102103	#[must_use]104	pub fn slice(105		self,106		from: Option<usize>,107		to: Option<usize>,108		step: Option<usize>,109	) -> Option<Self> {110		let len = self.len();111		let from = from.unwrap_or(0);112		let to = to.unwrap_or(len).min(len);113		let step = step.unwrap_or(1);114115		if from >= to || step == 0 {116			return None;117		}118119		Some(Self::new(SliceArray {120			inner: self,121			from: from as u32,122			to: to as u32,123			step: step as u32,124		}))125	}126127	/// Array length.128	pub fn len(&self) -> usize {129		self.0.len()130	}131132	/// Is array contains no elements?133	pub fn is_empty(&self) -> bool {134		self.0.is_empty()135	}136137	/// Get array element by index, evaluating it, if it is lazy.138	///139	/// Returns `None` on out-of-bounds condition.140	pub fn get(&self, index: usize) -> Result<Option<Val>> {141		self.0.get(index)142	}143144	/// Returns None if get is either non cheap, or out of bounds145	fn get_cheap(&self, index: usize) -> Option<Val> {146		self.0.get_cheap(index)147	}148149	/// Get array element by index, without evaluation.150	///151	/// Returns `None` on out-of-bounds condition.152	pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {153		self.0.get_lazy(index)154	}155156	pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {157		(0..self.len()).map(|i| self.get(i).transpose().expect("length checked"))158	}159160	/// Iterate over elements, returning lazy values.161	pub fn iter_lazy(&self) -> impl ArrayLikeIter<Thunk<Val>> + '_ {162		(0..self.len()).map(|i| self.get_lazy(i).expect("length checked"))163	}164165	pub fn iter_cheap(&self) -> Option<impl ArrayLikeIter<Val> + '_> {166		if self.is_cheap() {167			Some((0..self.len()).map(|i| self.get_cheap(i).expect("length and is_cheap checked")))168		} else {169			None170		}171	}172173	/// Return a reversed view on current array.174	#[must_use]175	pub fn reversed(self) -> Self {176		Self::new(ReverseArray(self))177	}178179	pub fn ptr_eq(a: &Self, b: &Self) -> bool {180		Cc::ptr_eq(&a.0, &b.0)181	}182183	/// Is this vec supports `.get_cheap()?`184	pub fn is_cheap(&self) -> bool {185		self.0.is_cheap()186	}187188	pub fn as_any(&self) -> &dyn Any {189		&self.0190	}191}192impl From<Vec<Val>> for ArrValue {193	fn from(value: Vec<Val>) -> Self {194		Self::eager(value)195	}196}197impl From<Vec<Thunk<Val>>> for ArrValue {198	fn from(value: Vec<Thunk<Val>>) -> Self {199		Self::lazy(value)200	}201}202impl FromIterator<Val> for ArrValue {203	fn from_iter<T: IntoIterator<Item = Val>>(iter: T) -> Self {204		Self::eager(iter.into_iter().collect())205	}206}207impl ArrayLike for ArrValue {208	fn len(&self) -> usize {209		self.0.len()210	}211212	fn get(&self, index: usize) -> Result<Option<Val>> {213		self.0.get(index)214	}215216	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {217		self.0.get_lazy(index)218	}219220	fn get_cheap(&self, index: usize) -> Option<Val> {221		self.0.get_cheap(index)222	}223224	fn is_cheap(&self) -> bool {225		self.0.is_cheap()226	}227}228229#[cfg(target_pointer_width = "64")]230static_assertions::assert_eq_size!(ArrValue, [u8; 8]);
modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/arr/spec.rs
+++ b/crates/jrsonnet-evaluator/src/arr/spec.rs
@@ -1,4 +1,4 @@
-use std::{cell::RefCell, iter, mem::replace, rc::Rc};
+use std::{any::Any, cell::RefCell, fmt::Debug, iter, mem::replace};
 
 use jrsonnet_gcmodule::{Cc, Trace};
 use jrsonnet_interner::{IBytes, IStr};
@@ -13,7 +13,7 @@
 	Context, Error, Result, Thunk, Val,
 };
 
-pub trait ArrayLike: Sized + Into<ArrValue> {
+pub trait ArrayLike: Any + Trace + Debug {
 	fn len(&self) -> usize;
 	fn is_empty(&self) -> bool {
 		self.len() == 0
@@ -22,12 +22,10 @@
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;
 	fn get_cheap(&self, index: usize) -> Option<Val>;
 
-	fn reverse(self) -> ArrValue {
-		ArrValue::Reverse(Cc::new(ReverseArray(self.into())))
-	}
+	fn is_cheap(&self) -> bool;
 }
 
-#[derive(Debug, Clone, Trace)]
+#[derive(Debug, Trace)]
 pub struct SliceArray {
 	pub(crate) inner: ArrValue,
 	pub(crate) from: u32,
@@ -81,15 +79,13 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.iter_cheap()?.nth(index)
 	}
-}
-impl From<SliceArray> for ArrValue {
-	fn from(value: SliceArray) -> Self {
-		Self::Slice(Cc::new(value))
+	fn is_cheap(&self) -> bool {
+		self.inner.is_cheap()
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
-pub struct CharArray(pub Rc<Vec<char>>);
+#[derive(Trace, Debug)]
+pub struct CharArray(pub Vec<char>);
 impl ArrayLike for CharArray {
 	fn len(&self) -> usize {
 		self.0.len()
@@ -108,14 +104,12 @@
 			.get(index)
 			.map(|v| Val::Str(StrValue::Flat(IStr::from(*v))))
 	}
-}
-impl From<CharArray> for ArrValue {
-	fn from(value: CharArray) -> Self {
-		ArrValue::Chars(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
+#[derive(Trace, Debug)]
 pub struct BytesArray(pub IBytes);
 impl ArrayLike for BytesArray {
 	fn len(&self) -> usize {
@@ -133,10 +127,8 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.0.get(index).map(|v| Val::Num(f64::from(*v)))
 	}
-}
-impl From<BytesArray> for ArrValue {
-	fn from(value: BytesArray) -> Self {
-		ArrValue::Bytes(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
@@ -148,30 +140,30 @@
 	Pending,
 }
 
-#[derive(Debug, Trace)]
-pub struct ExprArrayInner {
+#[derive(Debug, Trace, Clone)]
+pub struct ExprArray {
 	ctx: Context,
-	cached: RefCell<Vec<ArrayThunk<LocExpr>>>,
+	cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,
 }
-#[derive(Debug, Trace, Clone)]
-pub struct ExprArray(pub Cc<ExprArrayInner>);
 impl ExprArray {
 	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {
-		Self(Cc::new(ExprArrayInner {
+		Self {
 			ctx,
-			cached: RefCell::new(items.into_iter().map(ArrayThunk::Waiting).collect()),
-		}))
+			cached: Cc::new(RefCell::new(
+				items.into_iter().map(ArrayThunk::Waiting).collect(),
+			)),
+		}
 	}
 }
 impl ArrayLike for ExprArray {
 	fn len(&self) -> usize {
-		self.0.cached.borrow().len()
+		self.cached.borrow().len()
 	}
 	fn get(&self, index: usize) -> Result<Option<Val>> {
 		if index >= self.len() {
 			return Ok(None);
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
 			ArrayThunk::Errored(e) => return Err(e.clone()),
 			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
@@ -179,19 +171,19 @@
 		};
 
 		let ArrayThunk::Waiting(expr) =
-			replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
 		else {
 			unreachable!()
 		};
 
-		let new_value = match evaluate(self.0.ctx.clone(), &expr) {
+		let new_value = match evaluate(self.ctx.clone(), &expr) {
 			Ok(v) => v,
 			Err(e) => {
-				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
+				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
 				return Err(e);
 			}
 		};
-		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
+		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
 		Ok(Some(new_value))
 	}
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
@@ -215,7 +207,7 @@
 		if index >= self.len() {
 			return None;
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
 			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
 			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
@@ -229,14 +221,12 @@
 	fn get_cheap(&self, _index: usize) -> Option<Val> {
 		None
 	}
-}
-impl From<ExprArray> for ArrValue {
-	fn from(value: ExprArray) -> Self {
-		Self::Expr(value)
+	fn is_cheap(&self) -> bool {
+		false
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
+#[derive(Trace, Debug)]
 pub struct ExtendedArray {
 	pub a: ArrValue,
 	pub b: ArrValue,
@@ -319,15 +309,13 @@
 			self.b.get_cheap(index - self.split)
 		}
 	}
-}
-impl From<ExtendedArray> for ArrValue {
-	fn from(value: ExtendedArray) -> Self {
-		Self::Extended(Cc::new(value))
+	fn is_cheap(&self) -> bool {
+		self.a.is_cheap() && self.b.is_cheap()
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
-pub struct LazyArray(pub Cc<Vec<Thunk<Val>>>);
+#[derive(Trace, Debug)]
+pub struct LazyArray(pub Vec<Thunk<Val>>);
 impl ArrayLike for LazyArray {
 	fn len(&self) -> usize {
 		self.0.len()
@@ -344,15 +332,13 @@
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
 		self.0.get(index).cloned()
 	}
-}
-impl From<LazyArray> for ArrValue {
-	fn from(value: LazyArray) -> Self {
-		Self::Lazy(value)
+	fn is_cheap(&self) -> bool {
+		false
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
-pub struct EagerArray(pub Cc<Vec<Val>>);
+#[derive(Trace, Debug)]
+pub struct EagerArray(pub Vec<Val>);
 impl ArrayLike for EagerArray {
 	fn len(&self) -> usize {
 		self.0.len()
@@ -369,15 +355,13 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.0.get(index).cloned()
 	}
-}
-impl From<EagerArray> for ArrValue {
-	fn from(value: EagerArray) -> Self {
-		Self::Eager(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
 /// Inclusive range type
-#[derive(Debug, Trace, Clone, PartialEq, Eq)]
+#[derive(Debug, Trace, PartialEq, Eq)]
 pub struct RangeArray {
 	start: i32,
 	end: i32,
@@ -422,14 +406,12 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.range().nth(index).map(|i| Val::Num(f64::from(i)))
 	}
-}
-impl From<RangeArray> for ArrValue {
-	fn from(value: RangeArray) -> Self {
-		Self::Range(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
-#[derive(Debug, Trace, Clone)]
+#[derive(Debug, Trace)]
 pub struct ReverseArray(pub ArrValue);
 impl ArrayLike for ReverseArray {
 	fn len(&self) -> usize {
@@ -447,44 +429,37 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.0.get_cheap(self.0.len() - index - 1)
 	}
-	fn reverse(self) -> ArrValue {
-		self.0
-	}
-}
-impl From<ReverseArray> for ArrValue {
-	fn from(value: ReverseArray) -> Self {
-		Self::Reverse(Cc::new(value))
+	fn is_cheap(&self) -> bool {
+		self.0.is_cheap()
 	}
 }
 
-#[derive(Trace, Debug)]
-pub struct MappedArrayInner {
+#[derive(Trace, Debug, Clone)]
+pub struct MappedArray {
 	inner: ArrValue,
-	cached: RefCell<Vec<ArrayThunk<()>>>,
+	cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,
 	mapper: FuncVal,
 }
-#[derive(Trace, Debug, Clone)]
-pub struct MappedArray(Cc<MappedArrayInner>);
 impl MappedArray {
 	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {
 		let len = inner.len();
-		Self(Cc::new(MappedArrayInner {
+		Self {
 			inner,
-			cached: RefCell::new(vec![ArrayThunk::Waiting(()); len]),
+			cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),
 			mapper,
-		}))
+		}
 	}
 }
 impl ArrayLike for MappedArray {
 	fn len(&self) -> usize {
-		self.0.cached.borrow().len()
+		self.cached.borrow().len()
 	}
 
 	fn get(&self, index: usize) -> Result<Option<Val>> {
 		if index >= self.len() {
 			return Ok(None);
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
 			ArrayThunk::Errored(e) => return Err(e.clone()),
 			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
@@ -492,27 +467,26 @@
 		};
 
 		let ArrayThunk::Waiting(_) =
-			replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
 		else {
 			unreachable!()
 		};
 
 		let val = self
-			.0
 			.inner
 			.get(index)
 			.transpose()
 			.expect("index checked")
-			.and_then(|r| self.0.mapper.evaluate_simple(&(r,), false));
+			.and_then(|r| self.mapper.evaluate_simple(&(r,), false));
 
 		let new_value = match val {
 			Ok(v) => v,
 			Err(e) => {
-				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
+				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
 				return Err(e);
 			}
 		};
-		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
+		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
 		Ok(Some(new_value))
 	}
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
@@ -536,7 +510,7 @@
 		if index >= self.len() {
 			return None;
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
 			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
 			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
@@ -551,82 +525,54 @@
 	fn get_cheap(&self, _index: usize) -> Option<Val> {
 		None
 	}
-}
-impl From<MappedArray> for ArrValue {
-	fn from(value: MappedArray) -> Self {
-		Self::Mapped(value)
+	fn is_cheap(&self) -> bool {
+		false
 	}
 }
 
 #[derive(Trace, Debug)]
-pub struct RepeatedArrayInner {
+pub struct RepeatedArray {
 	data: ArrValue,
 	repeats: usize,
 	total_len: usize,
 }
-#[derive(Trace, Debug, Clone)]
-pub struct RepeatedArray(Cc<RepeatedArrayInner>);
 impl RepeatedArray {
 	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {
 		let total_len = data.len().checked_mul(repeats)?;
-		Some(Self(Cc::new(RepeatedArrayInner {
+		Some(Self {
 			data,
 			repeats,
 			total_len,
-		})))
-	}
-	pub fn is_cheap(&self) -> bool {
-		self.0.data.is_cheap()
+		})
 	}
 }
 
 impl ArrayLike for RepeatedArray {
 	fn len(&self) -> usize {
-		self.0.total_len
+		self.total_len
 	}
 
 	fn get(&self, index: usize) -> Result<Option<Val>> {
-		if index > self.0.total_len {
+		if index > self.total_len {
 			return Ok(None);
 		}
-		self.0.data.get(index % self.0.data.len())
+		self.data.get(index % self.data.len())
 	}
 
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
-		if index > self.0.total_len {
+		if index > self.total_len {
 			return None;
 		}
-		self.0.data.get_lazy(index % self.0.data.len())
+		self.data.get_lazy(index % self.data.len())
 	}
 
 	fn get_cheap(&self, index: usize) -> Option<Val> {
-		if index > self.0.total_len {
+		if index > self.total_len {
 			return None;
 		}
-		self.0.data.get_cheap(index % self.0.data.len())
+		self.data.get_cheap(index % self.data.len())
 	}
-}
-impl From<RepeatedArray> for ArrValue {
-	fn from(value: RepeatedArray) -> Self {
-		Self::Repeated(value)
+	fn is_cheap(&self) -> bool {
+		self.data.is_cheap()
 	}
 }
-
-macro_rules! pass {
-	($t:ident.$m:ident($($ident:ident),*)) => {
-		match $t {
-			Self::Bytes(e) => e.$m($($ident)*),
-			Self::Chars(e) => e.$m($($ident)*),
-			Self::Expr(e) => e.$m($($ident)*),
-			Self::Lazy(e) => e.$m($($ident)*),
-			Self::Eager(e) => e.$m($($ident)*),
-			Self::Range(e) => e.$m($($ident)*),
-			Self::Slice(e) => e.$m($($ident)*),
-			Self::Extended(e) => e.$m($($ident)*),
-			Self::Reverse(e) => e.$m($($ident)*),
-			Self::Mapped(e) => e.$m($($ident)*),
-			Self::Repeated(e) => e.$m($($ident)*),
-		}
-	};
-}
-pub(super) use pass;
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -573,10 +573,10 @@
 						evaluate(self.ctx, &self.item)
 					}
 				}
-				Val::Arr(ArrValue::lazy(Cc::new(vec![Thunk::new(ArrayElement {
+				Val::Arr(ArrValue::lazy(vec![Thunk::new(ArrayElement {
 					ctx,
 					item: items[0].clone(),
-				})])))
+				})]))
 			} else {
 				Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))
 			}
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -17,7 +17,7 @@
 	operator::evaluate_add_op,
 	tb, throw,
 	val::ThunkValue,
-	MaybeUnbound, Result, ResultExt, State, Thunk, Unbound, Val,
+	MaybeUnbound, Result, State, Thunk, Unbound, Val,
 };
 
 #[cfg(not(feature = "exp-preserve-order"))]
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -6,7 +6,7 @@
 use jrsonnet_types::{ComplexValType, ValType};
 
 use crate::{
-	arr::ArrValue,
+	arr::{ArrValue, BytesArray},
 	error::Result,
 	function::{native::NativeDesc, FuncDesc, FuncVal},
 	throw,
@@ -434,12 +434,13 @@
 	}
 
 	fn from_untyped(value: Val) -> Result<Self> {
-		if let Val::Arr(ArrValue::Bytes(bytes)) = value {
-			return Ok(bytes.0);
-		}
-		<Self as Typed>::TYPE.check(&value)?;
-		match value {
+		match &value {
 			Val::Arr(a) => {
+				if let Some(bytes) = a.as_any().downcast_ref::<BytesArray>() {
+					return Ok(bytes.0.as_slice().into());
+				};
+				<Self as Typed>::TYPE.check(&value)?;
+				// Any::downcast_ref::<ByteArray>(&a);
 				let mut out = Vec::with_capacity(a.len());
 				for e in a.iter() {
 					let r = e?;
@@ -447,7 +448,10 @@
 				}
 				Ok(out.as_slice().into())
 			}
-			_ => unreachable!(),
+			_ => {
+				<Self as Typed>::TYPE.check(&value)?;
+				unreachable!()
+			}
 		}
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -9,7 +9,7 @@
 use jrsonnet_interner::IStr;
 use jrsonnet_types::ValType;
 
-pub use crate::arr::ArrValue;
+pub use crate::arr::{ArrValue, ArrayLike};
 use crate::{
 	error::{Error, ErrorKind::*},
 	function::FuncVal,
modifiedcrates/jrsonnet-stdlib/src/sets.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/sets.rs
+++ b/crates/jrsonnet-stdlib/src/sets.rs
@@ -7,7 +7,6 @@
 	val::ArrValue,
 	Thunk, Val,
 };
-use jrsonnet_gcmodule::Cc;
 use jrsonnet_parser::BinaryOpType;
 
 #[builtin]
@@ -70,5 +69,5 @@
 			}
 		};
 	}
-	Ok(ArrValue::lazy(Cc::new(out)))
+	Ok(ArrValue::lazy(out))
 }
modifiedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -10,7 +10,6 @@
 	val::{equals, ArrValue},
 	Thunk, Val,
 };
-use jrsonnet_gcmodule::Cc;
 use jrsonnet_parser::BinaryOpType;
 
 use crate::eval_on_empty;
@@ -136,7 +135,7 @@
 			values.iter().collect::<Result<Vec<Val>>>()?,
 		)?))
 	} else {
-		Ok(ArrValue::lazy(Cc::new(sort_keyf(values, key_getter)?)))
+		Ok(ArrValue::lazy(sort_keyf(values, key_getter)?))
 	}
 }
 
@@ -186,7 +185,7 @@
 			arr.iter().collect::<Result<Vec<Val>>>()?,
 		)?))
 	} else {
-		Ok(ArrValue::lazy(Cc::new(uniq_keyf(arr, keyF)?)))
+		Ok(ArrValue::lazy(uniq_keyf(arr, keyF)?))
 	}
 }
 
@@ -204,8 +203,8 @@
 		Ok(ArrValue::eager(arr))
 	} else {
 		let arr = sort_keyf(arr, keyF.clone())?;
-		let arr = uniq_keyf(ArrValue::lazy(Cc::new(arr)), keyF)?;
-		Ok(ArrValue::lazy(Cc::new(arr)))
+		let arr = uniq_keyf(ArrValue::lazy(arr), keyF)?;
+		Ok(ArrValue::lazy(arr))
 	}
 }