lyng/docs/EfficientIterables.md

93 lines
3.8 KiB
Markdown

# Efficient Iterables in Kotlin Interop
Lyng provides high-performance iteration mechanisms that allow Kotlin-side code to interact with Lyng iterables efficiently and vice versa.
## 1. Enumerating Lyng Objects from Kotlin
To iterate over a Lyng object (like a `List`, `Set`, or `Range`) from Kotlin code, use the virtual `enumerate` method:
```kotlin
val lyngList: Obj = ...
lyngList.enumerate(scope) { item ->
println("Processing $item")
true // return true to continue, false to break
}
```
### Why it's efficient:
- **Zero allocation**: Unlike traditional iterators, it doesn't create a `LyngIterator` object or any intermediate wrappers.
- **Direct access**: Subclasses like `ObjList` override `enumerate` to iterate directly over their internal Kotlin collections.
- **Reduced overhead**: It avoids multiple `invokeInstanceMethod` calls for `hasNext()` and `next()` on every step, which would normally involve dynamic dispatch and scope overhead.
## 2. Reactive Enumeration with Flow
If you prefer a reactive approach or need to integrate with Kotlin Coroutines flows, use `toFlow()`:
```kotlin
lyngList.toFlow(scope).collect { item ->
// ...
}
```
*Note: `toFlow()` internally uses the Lyng iterator protocol (`iterator()`, `hasNext()`, `next()`), so it's slightly less efficient than `enumerate()` for performance-critical loops, but more idiomatic for flow-based processing.*
## 3. Creating Efficient Iterables for Lyng in Kotlin
When implementing a custom object in Kotlin that should be iterable in Lyng (e.g., usable in `for (x in myObj) { ... }`), follow these steps to ensure maximum performance.
### A. Inherit from `Obj` and use `ObjIterable`
Ensure your object's class has `ObjIterable` as a parent so the Lyng compiler recognizes it as an iterable.
```kotlin
class MyCollection(val items: List<Obj>) : Obj() {
override val objClass = MyCollection.type
companion object {
val type = ObjClass("MyCollection", ObjIterable).apply {
// Provide a Lyng-side iterator for compatibility with
// manual iterator usage in Lyng scripts.
// Using ObjKotlinObjIterator if items are already Obj instances:
addFn("iterator") {
ObjKotlinObjIterator(thisAs<MyCollection>().items.iterator())
}
}
}
}
```
### B. Override `enumerate` for Maximum Performance
The Lyng compiler's `for` loops use the `enumerate` method. By overriding it in your Kotlin class, you provide a "fast path" for iteration.
```kotlin
class MyCollection(val items: List<Obj>) : Obj() {
// ...
override suspend fun enumerate(scope: Scope, callback: suspend (Obj) -> Boolean) {
for (item in items) {
// If callback returns false, it means 'break' was called in Lyng
if (!callback(item)) break
}
}
}
```
### C. Use `ObjInt.of()` for Numeric Data
If your iterable contains integers, always use `ObjInt.of(Long)` instead of the `ObjInt(Long)` constructor. Lyng maintains a cache for small integers (-128 to 127), which significantly reduces object allocations and GC pressure during tight loops.
```kotlin
// Efficiently creating an integer object
val obj = ObjInt.of(42L)
// Or using extension methods which also use the cache:
val obj2 = 42.toObj()
val obj3 = 42L.toObj()
```
#### Note on `toObj()` extensions:
While `<reified T> T.toObj()` is convenient, using specific extensions like `Int.toObj()` or `Long.toObj()` is slightly more efficient as they use the `ObjInt` cache.
## 4. Summary of Best Practices
- **To Consume**: Use `enumerate(scope) { item -> ... true }`.
- **To Implement**: Override `enumerate` in your `Obj` subclass.
- **To Register**: Use `ObjIterable` (or `ObjCollection`) as a parent class in your `ObjClass` definition.
- **To Optimize**: Use `ObjInt.of()` (or `.toObj()`) for all integer object allocations.