Skip to content

Commit 117ab52

Browse files
authored
Allow iterator adaptors to be used as standalone functions (#404)
Backport of e2bf2b5 The instance checks in the `iterator` module use `KValue::is_iterable` to decide whether or not to accept the instance provided to the call. When used as standalone functions, adaptors like `iterator.enumerate(1..10)` were accepting the `iterator` module causing an error to be thrown. The fix is to extend the `is_iterable` logic to only accept object-like maps if they implement `@iterator` or `@next`.
1 parent e880c2a commit 117ab52

File tree

3 files changed

+25
-1
lines changed

3 files changed

+25
-1
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ The Koto project adheres to
88

99
## [0.15.1] Unreleased
1010

11+
### Fixed
12+
13+
#### Core Library
14+
15+
- Iterator adaptors can now be used as standalone functions.
16+
- e.g. `for i, n in iterator.enumerate 'abc'` would previously throw an error.
17+
1118
## [0.15.0] 2025.01.07
1219

1320
### Added

crates/runtime/src/types/value.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,15 @@ impl KValue {
136136
pub fn is_iterable(&self) -> bool {
137137
use KValue::*;
138138
match self {
139-
Range(_) | List(_) | Tuple(_) | Map(_) | Str(_) | Iterator(_) => true,
139+
Range(_) | List(_) | Tuple(_) | Str(_) | Iterator(_) => true,
140+
Map(m) => {
141+
if m.meta_map().is_some() {
142+
m.contains_meta_key(&UnaryOp::Iterator.into())
143+
|| m.contains_meta_key(&UnaryOp::Next.into())
144+
} else {
145+
true
146+
}
147+
}
140148
Object(o) => o
141149
.try_borrow()
142150
.is_ok_and(|o| !matches!(o.is_iterable(), IsIterable::NotIterable)),

crates/runtime/tests/iterator_tests.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ x.next().get()
170170
mod enumerate {
171171
use super::*;
172172

173+
#[test]
174+
fn as_standalone_function() {
175+
let script = "
176+
x = iterator.enumerate 'abc'
177+
x.next().get()
178+
";
179+
check_script_output(script, tuple(&[0.into(), "a".into()]));
180+
}
181+
173182
#[test]
174183
fn make_copy() {
175184
let script = "

0 commit comments

Comments
 (0)