Coverage Report

Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/ruchy/src/runtime/lazy.rs
Line
Count
Source
1
//! Lazy evaluation support for pipelines and operations
2
//!
3
//! This module implements lazy evaluation for performance optimization,
4
//! allowing operations to be composed without immediate execution.
5
6
use std::cell::RefCell;
7
use std::rc::Rc;
8
9
/// Represents a lazily evaluated value
10
pub enum LazyValue {
11
    /// Already computed value
12
    Computed(Value),
13
    /// Deferred computation (using Rc for shared ownership of closure)
14
    Deferred(Rc<RefCell<Option<Value>>>, Rc<dyn Fn() -> Result<Value>>),
15
    /// Lazy pipeline stage
16
    Pipeline {
17
        source: Box<LazyValue>,
18
        transform: Rc<dyn Fn(Value) -> Result<Value>>,
19
    },
20
}
21
22
impl Clone for LazyValue {
23
0
    fn clone(&self) -> Self {
24
0
        match self {
25
0
            LazyValue::Computed(v) => LazyValue::Computed(v.clone()),
26
0
            LazyValue::Deferred(cache, computation) => {
27
0
                LazyValue::Deferred(Rc::clone(cache), Rc::clone(computation))
28
            }
29
0
            LazyValue::Pipeline { source, transform } => LazyValue::Pipeline {
30
0
                source: source.clone(),
31
0
                transform: Rc::clone(transform),
32
0
            },
33
        }
34
0
    }
35
}
36
37
use crate::runtime::repl::Value;
38
use anyhow::Result;
39
40
impl LazyValue {
41
    /// Create a new computed lazy value
42
0
    pub fn computed(value: Value) -> Self {
43
0
        LazyValue::Computed(value)
44
0
    }
45
46
    /// Create a new deferred lazy value
47
0
    pub fn deferred<F>(computation: F) -> Self
48
0
    where
49
0
        F: Fn() -> Result<Value> + 'static,
50
    {
51
0
        LazyValue::Deferred(Rc::new(RefCell::new(None)), Rc::new(computation))
52
0
    }
53
54
    /// Create a pipeline transformation
55
0
    pub fn pipeline<F>(source: LazyValue, transform: F) -> Self
56
0
    where
57
0
        F: Fn(Value) -> Result<Value> + 'static,
58
    {
59
0
        LazyValue::Pipeline {
60
0
            source: Box::new(source),
61
0
            transform: Rc::new(transform),
62
0
        }
63
0
    }
64
65
    /// Force evaluation of the lazy value
66
    ///
67
    /// # Errors
68
    ///
69
    /// Returns an error if the computation fails
70
0
    pub fn force(&self) -> Result<Value> {
71
0
        match self {
72
0
            LazyValue::Computed(value) => Ok(value.clone()),
73
0
            LazyValue::Deferred(cache, computation) => {
74
                // Check if already computed
75
0
                if let Some(cached) = cache.borrow().as_ref() {
76
0
                    return Ok(cached.clone());
77
0
                }
78
79
                // Compute and cache
80
0
                let result = computation()?;
81
0
                *cache.borrow_mut() = Some(result.clone());
82
0
                Ok(result)
83
            }
84
0
            LazyValue::Pipeline { source, transform } => {
85
0
                let source_value = source.force()?;
86
0
                transform(source_value)
87
            }
88
        }
89
0
    }
90
91
    /// Check if the value has been computed
92
0
    pub fn is_computed(&self) -> bool {
93
0
        match self {
94
0
            LazyValue::Computed(_) => true,
95
0
            LazyValue::Deferred(cache, _) => cache.borrow().is_some(),
96
0
            LazyValue::Pipeline { .. } => false,
97
        }
98
0
    }
99
}
100
101
/// Type alias for filter predicates
102
type FilterPredicate = Box<dyn Fn(&Value) -> Result<bool>>;
103
/// Type alias for map transforms
104
type MapTransform = Box<dyn Fn(Value) -> Result<Value>>;
105
106
/// Lazy iterator for efficient collection processing
107
pub struct LazyIterator {
108
    /// Current state of the iterator
109
    state: RefCell<LazyIterState>,
110
}
111
112
enum LazyIterState {
113
    /// Source collection
114
    Source(Vec<Value>),
115
    /// Map transformation
116
    Map {
117
        source: Box<LazyIterator>,
118
        transform: MapTransform,
119
    },
120
    /// Filter transformation
121
    Filter {
122
        source: Box<LazyIterator>,
123
        predicate: FilterPredicate,
124
    },
125
    /// Take n elements
126
    Take {
127
        source: Box<LazyIterator>,
128
        count: usize,
129
    },
130
    /// Skip n elements
131
    Skip {
132
        source: Box<LazyIterator>,
133
        count: usize,
134
    },
135
}
136
137
impl LazyIterator {
138
    /// Create a new lazy iterator from a collection
139
0
    pub fn from_vec(values: Vec<Value>) -> Self {
140
0
        LazyIterator {
141
0
            state: RefCell::new(LazyIterState::Source(values)),
142
0
        }
143
0
    }
144
145
    /// Map transformation
146
    #[must_use]
147
0
    pub fn map<F>(self, transform: F) -> Self
148
0
    where
149
0
        F: Fn(Value) -> Result<Value> + 'static,
150
    {
151
0
        LazyIterator {
152
0
            state: RefCell::new(LazyIterState::Map {
153
0
                source: Box::new(self),
154
0
                transform: Box::new(transform),
155
0
            }),
156
0
        }
157
0
    }
158
159
    /// Filter transformation
160
    #[must_use]
161
0
    pub fn filter<F>(self, predicate: F) -> Self
162
0
    where
163
0
        F: Fn(&Value) -> Result<bool> + 'static,
164
    {
165
0
        LazyIterator {
166
0
            state: RefCell::new(LazyIterState::Filter {
167
0
                source: Box::new(self),
168
0
                predicate: Box::new(predicate),
169
0
            }),
170
0
        }
171
0
    }
172
173
    /// Take first n elements
174
    #[must_use]
175
0
    pub fn take(self, count: usize) -> Self {
176
0
        LazyIterator {
177
0
            state: RefCell::new(LazyIterState::Take {
178
0
                source: Box::new(self),
179
0
                count,
180
0
            }),
181
0
        }
182
0
    }
183
184
    /// Skip first n elements
185
    #[must_use]
186
0
    pub fn skip(self, count: usize) -> Self {
187
0
        LazyIterator {
188
0
            state: RefCell::new(LazyIterState::Skip {
189
0
                source: Box::new(self),
190
0
                count,
191
0
            }),
192
0
        }
193
0
    }
194
195
    /// Collect the iterator into a vector (forces evaluation)
196
    ///
197
    /// # Errors
198
    ///
199
    /// Returns an error if any transformation fails
200
0
    pub fn collect(&self) -> Result<Vec<Value>> {
201
0
        match &*self.state.borrow() {
202
0
            LazyIterState::Source(values) => Ok(values.clone()),
203
0
            LazyIterState::Map { source, transform } => {
204
0
                let source_values = source.collect()?;
205
0
                source_values
206
0
                    .into_iter()
207
0
                    .map(transform)
208
0
                    .collect::<Result<Vec<_>>>()
209
            }
210
0
            LazyIterState::Filter { source, predicate } => {
211
0
                let source_values = source.collect()?;
212
0
                let mut result = Vec::new();
213
0
                for value in source_values {
214
0
                    if predicate(&value)? {
215
0
                        result.push(value);
216
0
                    }
217
                }
218
0
                Ok(result)
219
            }
220
0
            LazyIterState::Take { source, count } => {
221
0
                let source_values = source.collect()?;
222
0
                Ok(source_values.into_iter().take(*count).collect())
223
            }
224
0
            LazyIterState::Skip { source, count } => {
225
0
                let source_values = source.collect()?;
226
0
                Ok(source_values.into_iter().skip(*count).collect())
227
            }
228
        }
229
0
    }
230
231
    /// Get the first element (forces minimal evaluation)
232
    ///
233
    /// # Errors
234
    ///
235
    /// Returns an error if evaluation fails
236
0
    pub fn first(&self) -> Result<Option<Value>> {
237
0
        let values = self.collect()?;
238
0
        Ok(values.into_iter().next())
239
0
    }
240
241
    /// Count elements (optimized to avoid full materialization where possible)
242
    ///
243
    /// # Errors
244
    ///
245
    /// Returns an error if evaluation fails
246
0
    pub fn count(&self) -> Result<usize> {
247
0
        match &*self.state.borrow() {
248
0
            LazyIterState::Source(values) => Ok(values.len()),
249
0
            _ => self.collect().map(|v| v.len()),
250
        }
251
0
    }
252
}
253
254
/// Lazy evaluation cache for memoization
255
pub struct LazyCache {
256
    cache: RefCell<std::collections::HashMap<String, Value>>,
257
}
258
259
impl LazyCache {
260
    /// Create a new lazy cache
261
0
    pub fn new() -> Self {
262
0
        LazyCache {
263
0
            cache: RefCell::new(std::collections::HashMap::new()),
264
0
        }
265
0
    }
266
267
    /// Get or compute a value
268
    ///
269
    /// # Errors
270
    ///
271
    /// Returns an error if computation fails
272
0
    pub fn get_or_compute<F>(&self, key: &str, compute: F) -> Result<Value>
273
0
    where
274
0
        F: FnOnce() -> Result<Value>,
275
    {
276
0
        if let Some(value) = self.cache.borrow().get(key) {
277
0
            return Ok(value.clone());
278
0
        }
279
280
0
        let value = compute()?;
281
0
        self.cache
282
0
            .borrow_mut()
283
0
            .insert(key.to_string(), value.clone());
284
0
        Ok(value)
285
0
    }
286
287
    /// Clear the cache
288
0
    pub fn clear(&self) {
289
0
        self.cache.borrow_mut().clear();
290
0
    }
291
292
    /// Get cache size
293
0
    pub fn size(&self) -> usize {
294
0
        self.cache.borrow().len()
295
0
    }
296
}
297
298
impl Default for LazyCache {
299
0
    fn default() -> Self {
300
0
        Self::new()
301
0
    }
302
}
303
304
#[cfg(test)]
305
#[allow(clippy::unwrap_used)]
306
mod tests {
307
    use super::*;
308
309
    #[test]
310
    fn test_lazy_value_computed() {
311
        let lazy = LazyValue::computed(Value::Int(42));
312
        assert!(lazy.is_computed());
313
        assert_eq!(lazy.force().unwrap(), Value::Int(42));
314
    }
315
316
    #[test]
317
    fn test_lazy_value_deferred() {
318
        let counter = Rc::new(RefCell::new(0));
319
        let counter_clone = Rc::clone(&counter);
320
321
        let lazy = LazyValue::deferred(move || {
322
            *counter_clone.borrow_mut() += 1;
323
            Ok(Value::Int(42))
324
        });
325
326
        assert!(!lazy.is_computed());
327
        assert_eq!(*counter.borrow(), 0);
328
329
        // First force computes
330
        assert_eq!(lazy.force().unwrap(), Value::Int(42));
331
        assert_eq!(*counter.borrow(), 1);
332
333
        // Second force uses cache
334
        assert_eq!(lazy.force().unwrap(), Value::Int(42));
335
        assert_eq!(*counter.borrow(), 1); // Not incremented again
336
    }
337
338
    #[test]
339
    fn test_lazy_iterator_map() {
340
        let values = vec![Value::Int(1), Value::Int(2), Value::Int(3)];
341
        let lazy = LazyIterator::from_vec(values).map(|v| {
342
            if let Value::Int(n) = v {
343
                Ok(Value::Int(n * 2))
344
            } else {
345
                Ok(v)
346
            }
347
        });
348
349
        let result = lazy.collect().unwrap();
350
        assert_eq!(result, vec![Value::Int(2), Value::Int(4), Value::Int(6)]);
351
    }
352
353
    #[test]
354
    fn test_lazy_iterator_filter() {
355
        let values = vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)];
356
        let lazy = LazyIterator::from_vec(values).filter(|v| {
357
            if let Value::Int(n) = v {
358
                Ok(n % 2 == 0)
359
            } else {
360
                Ok(false)
361
            }
362
        });
363
364
        let result = lazy.collect().unwrap();
365
        assert_eq!(result, vec![Value::Int(2), Value::Int(4)]);
366
    }
367
368
    #[test]
369
    fn test_lazy_cache() {
370
        let cache = LazyCache::new();
371
        let counter = Rc::new(RefCell::new(0));
372
373
        // First call computes
374
        let counter_clone = Rc::clone(&counter);
375
        let result = cache
376
            .get_or_compute("key", || {
377
                *counter_clone.borrow_mut() += 1;
378
                Ok(Value::Int(42))
379
            })
380
            .unwrap();
381
        assert_eq!(result, Value::Int(42));
382
        assert_eq!(*counter.borrow(), 1);
383
384
        // Second call uses cache
385
        let counter_clone = Rc::clone(&counter);
386
        let result = cache
387
            .get_or_compute("key", || {
388
                *counter_clone.borrow_mut() += 1;
389
                Ok(Value::Int(100))
390
            })
391
            .unwrap();
392
        assert_eq!(result, Value::Int(42)); // Cached value
393
        assert_eq!(*counter.borrow(), 1); // Not incremented
394
    }
395
}