feat(tvix/eval): implement builtins.map

As we already have a VM passed to the builtins, we can simply execute
the provided closure/lambda in it for each value.

The primary annoyance with this is that we have to clone the upvalues
for each element, but we can try making this cheaper in the
future (it's also a general problem in the VM itself).

Change-Id: I5bcf56d58c509c0eb081e7cf52f6093216451ce4
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6714
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
Vincent Ambo 2022-09-20 23:47:30 +03:00 committed by tazjin
parent 8207977648
commit 876c477256
4 changed files with 43 additions and 1 deletions

View file

@ -12,7 +12,8 @@ use std::{
use crate::{
errors::ErrorKind,
value::{Builtin, CoercionKind, NixAttrs, NixList, NixString, Value},
upvalues::UpvalueCarrier,
value::{Builtin, Closure, CoercionKind, NixAttrs, NixList, NixString, Value},
vm::VM,
};
@ -144,6 +145,21 @@ fn pure_builtins() -> Vec<Builtin> {
Builtin::new("length", &[true], |args, _| {
Ok(Value::Integer(args[0].to_list()?.len() as i64))
}),
Builtin::new("map", &[true, true], |args, vm| {
let list: NixList = args[1].to_list()?;
let func: Closure = args[0].to_closure()?;
list.into_iter()
.map(|val| {
// Leave the argument on the stack before calling the
// function.
vm.push(val);
vm.call(func.lambda(), func.upvalues().clone(), 1)
})
.collect::<Result<Vec<Value>, _>>()
.map(|list| Value::List(NixList::from(list)))
.map_err(Into::into)
}),
Builtin::new("hasAttr", &[true, true], |args, _| {
let k = args[0].to_str()?;
let xs = args[1].to_attrs()?;

View file

@ -100,6 +100,15 @@ impl From<ParseIntError> for ErrorKind {
}
}
/// Implementation used if errors occur while forcing thunks (which
/// can potentially be threaded through a few contexts, i.e. nested
/// thunks).
impl From<Error> for ErrorKind {
fn from(e: Error) -> Self {
Self::ThunkForce(Box::new(e))
}
}
#[derive(Clone, Debug)]
pub struct Error {
pub kind: ErrorKind,

View file

@ -0,0 +1 @@
[ [ 1 2 3 4 5 ] [ 2 4 6 8 10 ] [ 2 4 6 8 10 ] [ 1 2 3 4 5 ] ]

View file

@ -0,0 +1,16 @@
[
# identity function
(builtins.map (x: x) [ 1 2 3 4 5 ])
# double stuff
(builtins.map (x: x * 2) [ 1 2 3 4 5 ])
# same but with a closure this time
(
let n = 2;
in builtins.map (x: x * n) [ 1 2 3 4 5 ]
)
# from global scope
(map (x: x) [ 1 2 3 4 5 ])
]