Coverage Report

Created: 2025-09-05 15: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
1
    pub fn computed(value: Value) -> Self {
43
1
        LazyValue::Computed(value)
44
1
    }
45
46
    /// Create a new deferred lazy value
47
1
    pub fn deferred<F>(computation: F) -> Self
48
1
    where
49
1
        F: Fn() -> Result<Value> + 'static,
50
    {
51
1
        LazyValue::Deferred(Rc::new(RefCell::new(None)), Rc::new(computation))
52
1
    }
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
3
    pub fn force(&self) -> Result<Value> {
71
3
        match self {
72
1
            LazyValue::Computed(value) => Ok(value.clone()),
73
2
            LazyValue::Deferred(cache, computation) => {
74
                // Check if already computed
75
2
                if let Some(
cached1
) = cache.borrow().as_ref() {
76
1
                    return Ok(cached.clone());
77
1
                }
78
79
                // Compute and cache
80
1
                let result = computation()
?0
;
81
1
                *cache.borrow_mut() = Some(result.clone());
82
1
                Ok(result)
83
            }
84
0
            LazyValue::Pipeline { source, transform } => {
85
0
                let source_value = source.force()?;
86
0
                transform(source_value)
87
            }
88
        }
89
3
    }
90
91
    /// Check if the value has been computed
92
2
    pub fn is_computed(&self) -> bool {
93
2
        match self {
94
1
            LazyValue::Computed(_) => true,
95
1
            LazyValue::Deferred(cache, _) => cache.borrow().is_some(),
96
0
            LazyValue::Pipeline { .. } => false,
97
        }
98
2
    }
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
2
    pub fn from_vec(values: Vec<Value>) -> Self {
140
2
        LazyIterator {
141
2
            state: RefCell::new(LazyIterState::Source(values)),
142
2
        }
143
2
    }
144
145
    /// Map transformation
146
    #[must_use]
147
1
    pub fn map<F>(self, transform: F) -> Self
148
1
    where
149
1
        F: Fn(Value) -> Result<Value> + 'static,
150
    {
151
1
        LazyIterator {
152
1
            state: RefCell::new(LazyIterState::Map {
153
1
                source: Box::new(self),
154
1
                transform: Box::new(transform),
155
1
            }),
156
1
        }
157
1
    }
158
159
    /// Filter transformation
160
    #[must_use]
161
1
    pub fn filter<F>(self, predicate: F) -> Self
162
1
    where
163
1
        F: Fn(&Value) -> Result<bool> + 'static,
164
    {
165
1
        LazyIterator {
166
1
            state: RefCell::new(LazyIterState::Filter {
167
1
                source: Box::new(self),
168
1
                predicate: Box::new(predicate),
169
1
            }),
170
1
        }
171
1
    }
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
4
    pub fn collect(&self) -> Result<Vec<Value>> {
201
4
        match &*self.state.borrow() {
202
2
            LazyIterState::Source(values) => Ok(values.clone()),
203
1
            LazyIterState::Map { source, transform } => {
204
1
                let source_values = source.collect()
?0
;
205
1
                source_values
206
1
                    .into_iter()
207
1
                    .map(transform)
208
1
                    .collect::<Result<Vec<_>>>()
209
            }
210
1
            LazyIterState::Filter { source, predicate } => {
211
1
                let source_values = source.collect()
?0
;
212
1
                let mut result = Vec::new();
213
5
                for 
value4
in source_values {
214
4
                    if predicate(&value)
?0
{
215
2
                        result.push(value);
216
2
                    }
217
                }
218
1
                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
4
    }
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
1
    pub fn new() -> Self {
262
1
        LazyCache {
263
1
            cache: RefCell::new(std::collections::HashMap::new()),
264
1
        }
265
1
    }
266
267
    /// Get or compute a value
268
    ///
269
    /// # Errors
270
    ///
271
    /// Returns an error if computation fails
272
2
    pub fn get_or_compute<F>(&self, key: &str, compute: F) -> Result<Value>
273
2
    where
274
2
        F: FnOnce() -> Result<Value>,
275
    {
276
2
        if let Some(
value1
) = self.cache.borrow().get(key) {
277
1
            return Ok(value.clone());
278
1
        }
279
280
1
        let value = compute()
?0
;
281
1
        self.cache
282
1
            .borrow_mut()
283
1
            .insert(key.to_string(), value.clone());
284
1
        Ok(value)
285
2
    }
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
1
    fn test_lazy_value_computed() {
311
1
        let lazy = LazyValue::computed(Value::Int(42));
312
1
        assert!(lazy.is_computed());
313
1
        assert_eq!(lazy.force().unwrap(), Value::Int(42));
314
1
    }
315
316
    #[test]
317
1
    fn test_lazy_value_deferred() {
318
1
        let counter = Rc::new(RefCell::new(0));
319
1
        let counter_clone = Rc::clone(&counter);
320
321
1
        let lazy = LazyValue::deferred(move || {
322
1
            *counter_clone.borrow_mut() += 1;
323
1
            Ok(Value::Int(42))
324
1
        });
325
326
1
        assert!(!lazy.is_computed());
327
1
        assert_eq!(*counter.borrow(), 0);
328
329
        // First force computes
330
1
        assert_eq!(lazy.force().unwrap(), Value::Int(42));
331
1
        assert_eq!(*counter.borrow(), 1);
332
333
        // Second force uses cache
334
1
        assert_eq!(lazy.force().unwrap(), Value::Int(42));
335
1
        assert_eq!(*counter.borrow(), 1); // Not incremented again
336
1
    }
337
338
    #[test]
339
1
    fn test_lazy_iterator_map() {
340
1
        let values = vec![Value::Int(1), Value::Int(2), Value::Int(3)];
341
3
        let 
lazy1
=
LazyIterator::from_vec1
(
values1
).
map1
(|v| {
342
3
            if let Value::Int(n) = v {
343
3
                Ok(Value::Int(n * 2))
344
            } else {
345
0
                Ok(v)
346
            }
347
3
        });
348
349
1
        let result = lazy.collect().unwrap();
350
1
        assert_eq!(result, vec![Value::Int(2), Value::Int(4), Value::Int(6)]);
351
1
    }
352
353
    #[test]
354
1
    fn test_lazy_iterator_filter() {
355
1
        let values = vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)];
356
4
        let 
lazy1
=
LazyIterator::from_vec1
(
values1
).
filter1
(|v| {
357
4
            if let Value::Int(n) = v {
358
4
                Ok(n % 2 == 0)
359
            } else {
360
0
                Ok(false)
361
            }
362
4
        });
363
364
1
        let result = lazy.collect().unwrap();
365
1
        assert_eq!(result, vec![Value::Int(2), Value::Int(4)]);
366
1
    }
367
368
    #[test]
369
1
    fn test_lazy_cache() {
370
1
        let cache = LazyCache::new();
371
1
        let counter = Rc::new(RefCell::new(0));
372
373
        // First call computes
374
1
        let counter_clone = Rc::clone(&counter);
375
1
        let result = cache
376
1
            .get_or_compute("key", || {
377
1
                *counter_clone.borrow_mut() += 1;
378
1
                Ok(Value::Int(42))
379
1
            })
380
1
            .unwrap();
381
1
        assert_eq!(result, Value::Int(42));
382
1
        assert_eq!(*counter.borrow(), 1);
383
384
        // Second call uses cache
385
1
        let counter_clone = Rc::clone(&counter);
386
1
        let result = cache
387
1
            .get_or_compute("key", || 
{0
388
0
                *counter_clone.borrow_mut() += 1;
389
0
                Ok(Value::Int(100))
390
0
            })
391
1
            .unwrap();
392
1
        assert_eq!(result, Value::Int(42)); // Cached value
393
1
        assert_eq!(*counter.borrow(), 1); // Not incremented
394
1
    }
395
}