Skip to main content

pepl_stdlib/modules/
list.rs

1//! The `list` module — 32 functions (31 spec + 5 extensions + `some` alias).
2//!
3//! All operations are **immutable** — they return new lists, never mutate.
4//!
5//! ## Construction (4)
6//! | Function       | Signature                                    |
7//! |----------------|----------------------------------------------|
8//! | `list.empty`   | `() -> list`                                 |
9//! | `list.of`      | `(...items) -> list` (variadic)               |
10//! | `list.repeat`  | `(value, count: number) -> list`             |
11//! | `list.range`   | `(start: number, end: number) -> list`       |
12//!
13//! ## Access (5)
14//! | Function         | Signature                                  |
15//! |------------------|--------------------------------------------|
16//! | `list.length`    | `(items: list) -> number`                  |
17//! | `list.get`       | `(items: list, index: number) -> any\|nil` |
18//! | `list.first`     | `(items: list) -> any\|nil`                |
19//! | `list.last`      | `(items: list) -> any\|nil`                |
20//! | `list.index_of`  | `(items: list, value) -> number`           |
21//!
22//! ## Modification (10)
23//! | Function         | Signature                                            |
24//! |------------------|------------------------------------------------------|
25//! | `list.append`    | `(items: list, value) -> list`                       |
26//! | `list.prepend`   | `(items: list, value) -> list`                       |
27//! | `list.insert`    | `(items: list, index: number, value) -> list`        |
28//! | `list.remove`    | `(items: list, index: number) -> list`               |
29//! | `list.update`    | `(items: list, index: number, value) -> list`        |
30//! | `list.slice`     | `(items: list, start: number, end: number) -> list`  |
31//! | `list.concat`    | `(a: list, b: list) -> list`                         |
32//! | `list.reverse`   | `(items: list) -> list`                              |
33//! | `list.flatten`   | `(items: list) -> list`                              |
34//! | `list.unique`    | `(items: list) -> list`                              |
35//!
36//! ## Higher-Order (9)
37//! | Function           | Signature                                               |
38//! |--------------------|---------------------------------------------------------|
39//! | `list.map`         | `(items: list, f: fn(any) -> any) -> list`              |
40//! | `list.filter`      | `(items: list, pred: fn(any) -> bool) -> list`          |
41//! | `list.reduce`      | `(items: list, init, f: fn(acc, item) -> acc) -> any`   |
42//! | `list.find`        | `(items: list, pred: fn(any) -> bool) -> any\|nil`      |
43//! | `list.find_index`  | `(items: list, pred: fn(any) -> bool) -> number`        |
44//! | `list.every`       | `(items: list, pred: fn(any) -> bool) -> bool`          |
45//! | `list.any`         | `(items: list, pred: fn(any) -> bool) -> bool`          |
46//! | `list.sort`        | `(items: list, cmp: fn(a, b) -> number) -> list`        |
47//! | `list.count`       | `(items: list, pred: fn(any) -> bool) -> number`        |
48//!
49//! ## Query (4) — also non-higher-order
50//! | Function         | Signature                                  |
51//! |------------------|--------------------------------------------|
52//! | `list.contains`  | `(items: list, value) -> bool`             |
53//! | `list.zip`       | `(a: list, b: list) -> list`               |
54//! | `list.take`      | `(items: list, n: number) -> list`         |
55//! | `list.drop`      | `(items: list, n: number) -> list`         |
56
57use crate::error::StdlibError;
58use crate::module::StdlibModule;
59use crate::value::Value;
60
61/// The `list` stdlib module.
62pub struct ListModule;
63
64impl ListModule {
65    pub fn new() -> Self {
66        Self
67    }
68}
69
70impl Default for ListModule {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76impl StdlibModule for ListModule {
77    fn name(&self) -> &'static str {
78        "list"
79    }
80
81    fn has_function(&self, function: &str) -> bool {
82        matches!(
83            function,
84            // Construction
85            "empty" | "of" | "repeat" | "range"
86            // Access
87            | "length" | "get" | "first" | "last" | "index_of"
88            // Modification
89            | "append" | "prepend" | "insert" | "remove" | "update" | "set"
90            | "slice" | "concat" | "reverse" | "flatten" | "unique"
91            // Higher-order
92            | "map" | "filter" | "reduce" | "find" | "find_index"
93            | "every" | "any" | "some" | "sort" | "count"
94            // Query
95            | "contains" | "zip" | "take" | "drop"
96        )
97    }
98
99    fn call(&self, function: &str, args: Vec<Value>) -> Result<Value, StdlibError> {
100        match function {
101            // Construction
102            "empty" => self.empty(args),
103            "of" => self.of(args),
104            "repeat" => self.repeat(args),
105            "range" => self.range(args),
106            // Access
107            "length" => self.length(args),
108            "get" => self.get(args),
109            "first" => self.first(args),
110            "last" => self.last(args),
111            "index_of" => self.index_of(args),
112            // Modification
113            "append" => self.append(args),
114            "prepend" => self.prepend(args),
115            "insert" => self.insert(args),
116            "remove" => self.remove(args),
117            "update" | "set" => self.update(args),
118            "slice" => self.slice(args),
119            "concat" => self.concat(args),
120            "reverse" => self.reverse(args),
121            "flatten" => self.flatten(args),
122            "unique" => self.unique(args),
123            // Higher-order
124            "map" => self.map(args),
125            "filter" => self.filter(args),
126            "reduce" => self.reduce(args),
127            "find" => self.find(args),
128            "find_index" => self.find_index(args),
129            "every" => self.every(args),
130            "any" | "some" => self.any(args),
131            "sort" => self.sort(args),
132            "count" => self.count(args),
133            // Query
134            "contains" => self.contains(args),
135            "zip" => self.zip(args),
136            "take" => self.take(args),
137            "drop" => self.drop_fn(args),
138            _ => Err(StdlibError::unknown_function("list", function)),
139        }
140    }
141}
142
143// ── Helpers ───────────────────────────────────────────────────────────────────
144
145/// Extract a single list argument.
146fn expect_list(fn_name: &str, args: &[Value]) -> Result<Vec<Value>, StdlibError> {
147    if args.len() != 1 {
148        return Err(StdlibError::wrong_args(fn_name, 1, args.len()));
149    }
150    match &args[0] {
151        Value::List(items) => Ok(items.clone()),
152        other => Err(StdlibError::type_mismatch(fn_name, 1, "list", other.type_name())),
153    }
154}
155
156/// Extract a list from the first argument (multi-arg functions).
157fn extract_list(fn_name: &str, val: &Value) -> Result<Vec<Value>, StdlibError> {
158    match val {
159        Value::List(items) => Ok(items.clone()),
160        other => Err(StdlibError::type_mismatch(fn_name, 1, "list", other.type_name())),
161    }
162}
163
164/// Extract an integer index from a Value, checking it's a whole number.
165fn extract_index(fn_name: &str, val: &Value, position: usize) -> Result<i64, StdlibError> {
166    match val {
167        Value::Number(n) => {
168            if n.fract() != 0.0 || !n.is_finite() {
169                return Err(StdlibError::RuntimeError(format!(
170                    "{fn_name}: index must be a whole number, got {n}"
171                )));
172            }
173            Ok(*n as i64)
174        }
175        other => Err(StdlibError::type_mismatch(fn_name, position, "number", other.type_name())),
176    }
177}
178
179/// Extract a number argument at a given position.
180fn extract_number(fn_name: &str, val: &Value, position: usize) -> Result<f64, StdlibError> {
181    match val {
182        Value::Number(n) => Ok(*n),
183        other => Err(StdlibError::type_mismatch(fn_name, position, "number", other.type_name())),
184    }
185}
186
187/// Extract a function argument at a given position.
188fn extract_function(
189    fn_name: &str,
190    val: &Value,
191    position: usize,
192) -> Result<crate::value::StdlibFn, StdlibError> {
193    match val {
194        Value::Function(f) => Ok(f.clone()),
195        other => Err(StdlibError::type_mismatch(
196            fn_name,
197            position,
198            "function",
199            other.type_name(),
200        )),
201    }
202}
203
204// ── Construction ──────────────────────────────────────────────────────────────
205
206impl ListModule {
207    /// `list.empty() -> list` — returns an empty list.
208    fn empty(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
209        if !args.is_empty() {
210            return Err(StdlibError::wrong_args("list.empty", 0, args.len()));
211        }
212        Ok(Value::List(vec![]))
213    }
214
215    /// `list.of(...items) -> list` — creates a list from all arguments (variadic).
216    fn of(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
217        Ok(Value::List(args))
218    }
219
220    /// `list.repeat(value, count) -> list` — creates a list of `count` copies of `value`.
221    fn repeat(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
222        if args.len() != 2 {
223            return Err(StdlibError::wrong_args("list.repeat", 2, args.len()));
224        }
225        let count = extract_number("list.repeat", &args[1], 2)?;
226        if count.fract() != 0.0 || !count.is_finite() || count < 0.0 {
227            return Err(StdlibError::RuntimeError(
228                "list.repeat: count must be a non-negative integer".to_string(),
229            ));
230        }
231        let count = count as usize;
232        let item = args[0].clone();
233        Ok(Value::List(vec![item; count]))
234    }
235
236    /// `list.range(start, end) -> list<number>` — start inclusive, end exclusive.
237    fn range(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
238        if args.len() != 2 {
239            return Err(StdlibError::wrong_args("list.range", 2, args.len()));
240        }
241        let start = extract_number("list.range", &args[0], 1)?;
242        let end = extract_number("list.range", &args[1], 2)?;
243        if start.fract() != 0.0 || end.fract() != 0.0 || !start.is_finite() || !end.is_finite() {
244            return Err(StdlibError::RuntimeError(
245                "list.range: start and end must be integers".to_string(),
246            ));
247        }
248        let start = start as i64;
249        let end = end as i64;
250        if end < start {
251            return Ok(Value::List(vec![]));
252        }
253        // Safety limit: prevent absurdly large ranges
254        let len = (end - start) as usize;
255        if len > 10_000_000 {
256            return Err(StdlibError::RuntimeError(
257                "list.range: range too large (max 10,000,000 elements)".to_string(),
258            ));
259        }
260        let items: Vec<Value> = (start..end).map(|i| Value::Number(i as f64)).collect();
261        Ok(Value::List(items))
262    }
263
264    // ── Access ────────────────────────────────────────────────────────────────
265
266    /// `list.length(items) -> number`
267    fn length(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
268        let items = expect_list("list.length", &args)?;
269        Ok(Value::Number(items.len() as f64))
270    }
271
272    /// `list.get(items, index) -> any|nil` — returns nil on out-of-bounds.
273    fn get(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
274        if args.len() != 2 {
275            return Err(StdlibError::wrong_args("list.get", 2, args.len()));
276        }
277        let items = extract_list("list.get", &args[0])?;
278        let index = extract_index("list.get", &args[1], 2)?;
279        if index < 0 || index as usize >= items.len() {
280            Ok(Value::Nil)
281        } else {
282            Ok(items[index as usize].clone())
283        }
284    }
285
286    /// `list.first(items) -> any|nil` — returns nil for empty list.
287    fn first(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
288        let items = expect_list("list.first", &args)?;
289        Ok(items.first().cloned().unwrap_or(Value::Nil))
290    }
291
292    /// `list.last(items) -> any|nil` — returns nil for empty list.
293    fn last(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
294        let items = expect_list("list.last", &args)?;
295        Ok(items.last().cloned().unwrap_or(Value::Nil))
296    }
297
298    /// `list.index_of(items, value) -> number` — returns -1 if not found.
299    fn index_of(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
300        if args.len() != 2 {
301            return Err(StdlibError::wrong_args("list.index_of", 2, args.len()));
302        }
303        let items = extract_list("list.index_of", &args[0])?;
304        let needle = &args[1];
305        let index = items
306            .iter()
307            .position(|v| v == needle)
308            .map(|i| i as f64)
309            .unwrap_or(-1.0);
310        Ok(Value::Number(index))
311    }
312
313    // ── Modification ──────────────────────────────────────────────────────────
314
315    /// `list.append(items, value) -> list` — adds to end.
316    fn append(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
317        if args.len() != 2 {
318            return Err(StdlibError::wrong_args("list.append", 2, args.len()));
319        }
320        let mut items = extract_list("list.append", &args[0])?;
321        items.push(args[1].clone());
322        Ok(Value::List(items))
323    }
324
325    /// `list.prepend(items, value) -> list` — adds to start.
326    fn prepend(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
327        if args.len() != 2 {
328            return Err(StdlibError::wrong_args("list.prepend", 2, args.len()));
329        }
330        let mut items = extract_list("list.prepend", &args[0])?;
331        items.insert(0, args[1].clone());
332        Ok(Value::List(items))
333    }
334
335    /// `list.insert(items, index, value) -> list` — inserts at index.
336    fn insert(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
337        if args.len() != 3 {
338            return Err(StdlibError::wrong_args("list.insert", 3, args.len()));
339        }
340        let mut items = extract_list("list.insert", &args[0])?;
341        let index = extract_index("list.insert", &args[1], 2)?;
342        if index < 0 || index as usize > items.len() {
343            return Err(StdlibError::RuntimeError(format!(
344                "list.insert: index {} out of bounds for list of length {}",
345                index,
346                items.len()
347            )));
348        }
349        items.insert(index as usize, args[2].clone());
350        Ok(Value::List(items))
351    }
352
353    /// `list.remove(items, index) -> list` — removes element at index.
354    fn remove(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
355        if args.len() != 2 {
356            return Err(StdlibError::wrong_args("list.remove", 2, args.len()));
357        }
358        let mut items = extract_list("list.remove", &args[0])?;
359        let index = extract_index("list.remove", &args[1], 2)?;
360        if index < 0 || index as usize >= items.len() {
361            return Err(StdlibError::RuntimeError(format!(
362                "list.remove: index {} out of bounds for list of length {}",
363                index,
364                items.len()
365            )));
366        }
367        items.remove(index as usize);
368        Ok(Value::List(items))
369    }
370
371    /// `list.update(items, index, value) -> list` — replaces element at index.
372    fn update(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
373        if args.len() != 3 {
374            return Err(StdlibError::wrong_args("list.update", 3, args.len()));
375        }
376        let mut items = extract_list("list.update", &args[0])?;
377        let index = extract_index("list.update", &args[1], 2)?;
378        if index < 0 || index as usize >= items.len() {
379            return Err(StdlibError::RuntimeError(format!(
380                "list.update: index {} out of bounds for list of length {}",
381                index,
382                items.len()
383            )));
384        }
385        items[index as usize] = args[2].clone();
386        Ok(Value::List(items))
387    }
388
389    /// `list.slice(items, start, end) -> list` — start inclusive, end exclusive.
390    fn slice(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
391        if args.len() != 3 {
392            return Err(StdlibError::wrong_args("list.slice", 3, args.len()));
393        }
394        let items = extract_list("list.slice", &args[0])?;
395        let start = extract_index("list.slice", &args[1], 2)?;
396        let end = extract_index("list.slice", &args[2], 3)?;
397        let len = items.len() as i64;
398        // Clamp to bounds
399        let start = start.clamp(0, len) as usize;
400        let end = end.clamp(0, len) as usize;
401        if start >= end {
402            return Ok(Value::List(vec![]));
403        }
404        Ok(Value::List(items[start..end].to_vec()))
405    }
406
407    /// `list.concat(a, b) -> list` — concatenates two lists.
408    fn concat(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
409        if args.len() != 2 {
410            return Err(StdlibError::wrong_args("list.concat", 2, args.len()));
411        }
412        let mut a = extract_list("list.concat", &args[0])?;
413        let b = match &args[1] {
414            Value::List(items) => items.clone(),
415            other => {
416                return Err(StdlibError::type_mismatch(
417                    "list.concat",
418                    2,
419                    "list",
420                    other.type_name(),
421                ))
422            }
423        };
424        a.extend(b);
425        Ok(Value::List(a))
426    }
427
428    /// `list.reverse(items) -> list`
429    fn reverse(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
430        let mut items = expect_list("list.reverse", &args)?;
431        items.reverse();
432        Ok(Value::List(items))
433    }
434
435    /// `list.flatten(items) -> list` — flattens one level of nesting.
436    fn flatten(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
437        let items = expect_list("list.flatten", &args)?;
438        let mut result = Vec::new();
439        for item in items {
440            match item {
441                Value::List(inner) => result.extend(inner),
442                other => result.push(other),
443            }
444        }
445        Ok(Value::List(result))
446    }
447
448    /// `list.unique(items) -> list` — removes duplicates, preserving first occurrence.
449    fn unique(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
450        let items = expect_list("list.unique", &args)?;
451        let mut seen = Vec::new();
452        let mut result = Vec::new();
453        for item in items {
454            if !seen.contains(&item) {
455                seen.push(item.clone());
456                result.push(item);
457            }
458        }
459        Ok(Value::List(result))
460    }
461
462    // ── Higher-Order ──────────────────────────────────────────────────────────
463
464    /// `list.map(items, f) -> list` — applies f to each element.
465    fn map(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
466        if args.len() != 2 {
467            return Err(StdlibError::wrong_args("list.map", 2, args.len()));
468        }
469        let items = extract_list("list.map", &args[0])?;
470        let f = extract_function("list.map", &args[1], 2)?;
471        let mut result = Vec::with_capacity(items.len());
472        for item in items {
473            result.push(f.call(vec![item])?);
474        }
475        Ok(Value::List(result))
476    }
477
478    /// `list.filter(items, predicate) -> list`
479    fn filter(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
480        if args.len() != 2 {
481            return Err(StdlibError::wrong_args("list.filter", 2, args.len()));
482        }
483        let items = extract_list("list.filter", &args[0])?;
484        let pred = extract_function("list.filter", &args[1], 2)?;
485        let mut result = Vec::new();
486        for item in items {
487            let keep = pred.call(vec![item.clone()])?;
488            if keep.is_truthy() {
489                result.push(item);
490            }
491        }
492        Ok(Value::List(result))
493    }
494
495    /// `list.reduce(items, initial, f) -> any`
496    fn reduce(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
497        if args.len() != 3 {
498            return Err(StdlibError::wrong_args("list.reduce", 3, args.len()));
499        }
500        let items = extract_list("list.reduce", &args[0])?;
501        let mut acc = args[1].clone();
502        let f = extract_function("list.reduce", &args[2], 3)?;
503        for item in items {
504            acc = f.call(vec![acc, item])?;
505        }
506        Ok(acc)
507    }
508
509    /// `list.find(items, predicate) -> any|nil` — returns first match or nil.
510    fn find(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
511        if args.len() != 2 {
512            return Err(StdlibError::wrong_args("list.find", 2, args.len()));
513        }
514        let items = extract_list("list.find", &args[0])?;
515        let pred = extract_function("list.find", &args[1], 2)?;
516        for item in items {
517            let matches = pred.call(vec![item.clone()])?;
518            if matches.is_truthy() {
519                return Ok(item);
520            }
521        }
522        Ok(Value::Nil)
523    }
524
525    /// `list.find_index(items, predicate) -> number` — returns -1 if not found.
526    fn find_index(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
527        if args.len() != 2 {
528            return Err(StdlibError::wrong_args("list.find_index", 2, args.len()));
529        }
530        let items = extract_list("list.find_index", &args[0])?;
531        let pred = extract_function("list.find_index", &args[1], 2)?;
532        for (i, item) in items.into_iter().enumerate() {
533            let matches = pred.call(vec![item])?;
534            if matches.is_truthy() {
535                return Ok(Value::Number(i as f64));
536            }
537        }
538        Ok(Value::Number(-1.0))
539    }
540
541    /// `list.every(items, predicate) -> bool` — true if pred holds for all.
542    fn every(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
543        if args.len() != 2 {
544            return Err(StdlibError::wrong_args("list.every", 2, args.len()));
545        }
546        let items = extract_list("list.every", &args[0])?;
547        let pred = extract_function("list.every", &args[1], 2)?;
548        for item in items {
549            let result = pred.call(vec![item])?;
550            if !result.is_truthy() {
551                return Ok(Value::Bool(false));
552            }
553        }
554        Ok(Value::Bool(true))
555    }
556
557    /// `list.any(items, predicate) -> bool` — true if pred holds for any element.
558    /// Also available as `list.some` (backward-compat alias).
559    fn any(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
560        if args.len() != 2 {
561            return Err(StdlibError::wrong_args("list.any", 2, args.len()));
562        }
563        let items = extract_list("list.any", &args[0])?;
564        let pred = extract_function("list.any", &args[1], 2)?;
565        for item in items {
566            let result = pred.call(vec![item])?;
567            if result.is_truthy() {
568                return Ok(Value::Bool(true));
569            }
570        }
571        Ok(Value::Bool(false))
572    }
573
574    /// `list.sort(items, compare) -> list` — stable sort using comparator.
575    ///
576    /// The comparator `fn(a, b) -> number` must return:
577    /// - negative if a < b
578    /// - zero if a == b
579    /// - positive if a > b
580    fn sort(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
581        if args.len() != 2 {
582            return Err(StdlibError::wrong_args("list.sort", 2, args.len()));
583        }
584        let mut items = extract_list("list.sort", &args[0])?;
585        let cmp = extract_function("list.sort", &args[1], 2)?;
586
587        // We need to propagate errors from the comparator, so we use a cell
588        // to capture the first error that occurs during sorting.
589        let mut sort_error: Option<StdlibError> = None;
590
591        items.sort_by(|a, b| {
592            if sort_error.is_some() {
593                return std::cmp::Ordering::Equal;
594            }
595            match cmp.call(vec![a.clone(), b.clone()]) {
596                Ok(Value::Number(n)) => {
597                    if n < 0.0 {
598                        std::cmp::Ordering::Less
599                    } else if n > 0.0 {
600                        std::cmp::Ordering::Greater
601                    } else {
602                        std::cmp::Ordering::Equal
603                    }
604                }
605                Ok(other) => {
606                    sort_error = Some(StdlibError::RuntimeError(format!(
607                        "list.sort: comparator must return a number, got {}",
608                        other.type_name()
609                    )));
610                    std::cmp::Ordering::Equal
611                }
612                Err(e) => {
613                    sort_error = Some(e);
614                    std::cmp::Ordering::Equal
615                }
616            }
617        });
618
619        if let Some(e) = sort_error {
620            return Err(e);
621        }
622        Ok(Value::List(items))
623    }
624
625    /// `list.count(items, predicate) -> number` — counts elements matching pred.
626    fn count(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
627        if args.len() != 2 {
628            return Err(StdlibError::wrong_args("list.count", 2, args.len()));
629        }
630        let items = extract_list("list.count", &args[0])?;
631        let pred = extract_function("list.count", &args[1], 2)?;
632        let mut n = 0usize;
633        for item in items {
634            let result = pred.call(vec![item])?;
635            if result.is_truthy() {
636                n += 1;
637            }
638        }
639        Ok(Value::Number(n as f64))
640    }
641
642    // ── Query ─────────────────────────────────────────────────────────────────
643
644    /// `list.contains(items, value) -> bool` — value equality check.
645    fn contains(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
646        if args.len() != 2 {
647            return Err(StdlibError::wrong_args("list.contains", 2, args.len()));
648        }
649        let items = extract_list("list.contains", &args[0])?;
650        let needle = &args[1];
651        Ok(Value::Bool(items.contains(needle)))
652    }
653
654    /// `list.zip(a, b) -> list` — pairs elements from two lists into records.
655    ///
656    /// Returns a list of `{ first, second }` records. If lists differ in length,
657    /// stops at the shorter list.
658    fn zip(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
659        if args.len() != 2 {
660            return Err(StdlibError::wrong_args("list.zip", 2, args.len()));
661        }
662        let a = extract_list("list.zip", &args[0])?;
663        let b = match &args[1] {
664            Value::List(items) => items.clone(),
665            other => {
666                return Err(StdlibError::type_mismatch(
667                    "list.zip",
668                    2,
669                    "list",
670                    other.type_name(),
671                ))
672            }
673        };
674        let result: Vec<Value> = a
675            .into_iter()
676            .zip(b)
677            .map(|(first, second)| {
678                let mut fields = std::collections::BTreeMap::new();
679                fields.insert("first".to_string(), first);
680                fields.insert("second".to_string(), second);
681                Value::record(fields)
682            })
683            .collect();
684        Ok(Value::List(result))
685    }
686
687    /// `list.take(items, n) -> list` — takes first n elements.
688    fn take(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
689        if args.len() != 2 {
690            return Err(StdlibError::wrong_args("list.take", 2, args.len()));
691        }
692        let items = extract_list("list.take", &args[0])?;
693        let n = extract_number("list.take", &args[1], 2)?;
694        if n.fract() != 0.0 || !n.is_finite() || n < 0.0 {
695            return Err(StdlibError::RuntimeError(
696                "list.take: count must be a non-negative integer".to_string(),
697            ));
698        }
699        let n = (n as usize).min(items.len());
700        Ok(Value::List(items[..n].to_vec()))
701    }
702
703    /// `list.drop(items, n) -> list` — returns all elements after the first n.
704    ///
705    /// Spec: `list.drop(xs: list<T>, count: number) -> list<T>`
706    /// If count >= length, returns empty list. If count <= 0, returns full list.
707    fn drop_fn(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
708        if args.len() != 2 {
709            return Err(StdlibError::wrong_args("list.drop", 2, args.len()));
710        }
711        let items = extract_list("list.drop", &args[0])?;
712        let n = extract_number("list.drop", &args[1], 2)?;
713        if n.fract() != 0.0 || !n.is_finite() || n < 0.0 {
714            return Err(StdlibError::RuntimeError(
715                "list.drop: count must be a non-negative integer".to_string(),
716            ));
717        }
718        let n = (n as usize).min(items.len());
719        Ok(Value::List(items[n..].to_vec()))
720    }
721}