I've been trying to figure out how to implement something like Rust's .next() and .peek() explicit-iterator methods effectfully.
In the fake language I've been using to prototype things before implementing them in Effekt, considering continuations as first-class values that are mutated by .resume() and can be assigned to seems to allow for semantics compatible with both effectful, lazy iterators and also explicit .next() and .peek() methods. That linearity / ownership is preserved seems very hard to deal with (but possible?? i saw a recent paper) but also not super relevant for the main conceptual issue I'm hitting.
pub effect Yield[T] {
yield(T)
}
pub func iter[T](self: List[T]) {
if let Cons(head, tail) = self {
do yield(head)
tail.iter()
}
}
pub func next[T](self: mut Continuation[()] / Yield[T]): T? {
// mutates `self`. thus, multiple calls to `.next()` can be chained
try self.resume() {
// ownership over `self` is given up upon catching an effect, or evaluating to a value
yield(x) => some(x)
// this is matching the value `()` (empty tuple) of type `()` (unit)
() => none
}
}
pub func peek[T](self: mut Continuation[()] / Yield[T]): T? {
// we mutate self here.
try self.resume() {
// upon catching a raised effect, `self` is no longer being mutated.
yield(x) => {
func wrapped() {
do yield(x)
// so we can uniquely mutate `self` again here to resume.
self.resume()
}
// this might not work wrt. ownership and also efficiency
self = Continuation(wrapped())
some(x)
}
_ => none
}
}
// An explicit Continuation constructor converts this computation of type () to a Continuation[()].
// Really, this would be implicit, I think - any func taking in a Continuation would wrap its arg in this.
// This is kinda similar to Effekt's blocks??
let iterator = Continuation([1, 2, 3].iter())
assert iterator.next() is some(1)
assert iterator.next() is some(2)
assert iterator.peek() is some(3)
assert iterator.next() is some(3)
assert iterator.next() is none
But this is very far from Effekt's semantics... since there isn't an explicit Continuation type, and instead computations that are treated as zero-arity functions when passed to a function through a {} block, and are called as a standard function instead of with .resume(), with the target of the resume keyword instead implicit in the effect handler. So I'm kind of wondering, is this sort of thing possible to express in Effekt? Are there different features of Effekt's effect-and-handler system that I haven't stumbled across that might be relevant here? Or is this entirely the wrong approach to the issue, conceptually, and does something like .next() and .peek() necessitate operating on some concrete iterator struct?
I've been trying to figure out how to implement something like Rust's
.next()and.peek()explicit-iterator methods effectfully.In the fake language I've been using to prototype things before implementing them in Effekt, considering continuations as first-class values that are mutated by
.resume()and can be assigned to seems to allow for semantics compatible with both effectful, lazy iterators and also explicit.next()and.peek()methods. That linearity / ownership is preserved seems very hard to deal with (but possible?? i saw a recent paper) but also not super relevant for the main conceptual issue I'm hitting.But this is very far from Effekt's semantics... since there isn't an explicit Continuation type, and instead computations that are treated as zero-arity functions when passed to a function through a
{}block, and are called as a standard function instead of with.resume(), with the target of theresumekeyword instead implicit in the effect handler. So I'm kind of wondering, is this sort of thing possible to express in Effekt? Are there different features of Effekt's effect-and-handler system that I haven't stumbled across that might be relevant here? Or is this entirely the wrong approach to the issue, conceptually, and does something like.next()and.peek()necessitate operating on some concrete iterator struct?