Skip to content

Instantly share code, notes, and snippets.

@osa1
Last active June 5, 2025 16:52
Show Gist options
  • Save osa1/7506e9a5074dc54f75c09e5767209f86 to your computer and use it in GitHub Desktop.
Save osa1/7506e9a5074dc54f75c09e5767209f86 to your computer and use it in GitHub Desktop.
abstract class Sequence<Element> {
void forEach(void Function(Element) consumer);
}
class CountFrom implements Sequence<int> {
final int from;
CountFrom(this.from);
@override
void forEach(void Function(int) consumer) {
for (int i = from; ; i += 1) {
consumer(i);
}
}
}
class Empty implements Sequence<int> {
@override
void forEach(void Function(int) consumer) {}
}
class AppendAfter<Element> implements Sequence<Element> {
final Sequence<Element> first;
final Sequence<Element> second;
final int amount;
AppendAfter(this.first, this.amount, this.second);
@override
void forEach(void Function(Element) consumer) {
try {
int count = amount;
first.forEach((element) {
// Note: if you change the `count--` below to update `count` when not
// throwing an exception, this works as expected.
//
// The point is, outer `AppendAfter` handler throws an exception that
// is caught by the inner `AppendAfter`, which then leaves inner
// `AppendAfter` in an invalid state where `count` is negative.
if (count-- == 0) {
throw AppendAfterException();
}
consumer(element);
});
} on AppendAfterException {}
second.forEach(consumer);
}
}
class AppendAfterException {}
void main() {
// final simple = AppendAfter(CountFrom(0), 5, Empty());
// simple.forEach((i) => print(i));
final complex = AppendAfter(AppendAfter(CountFrom(0), 10, CountFrom(20)), 5, Empty());
complex.forEach((i) => print(i));
}
trait Sequence[seq, t, exn]:
forEach(self: seq, consumer: Fn(t) / exn) / exn
# ------------------------------------------------------------------------------
type CountFrom:
from: U32
impl Sequence[CountFrom, U32, exn]:
forEach(self: CountFrom, consumer: Fn(U32) / exn) / exn:
let i = self.from
loop:
consumer(i)
i += 1
# ------------------------------------------------------------------------------
type AppendAfter[s1, s2]:
seq1: s1
seq2: s2
amt: U32
impl[Sequence[s1, t, [AppendAfterStop, ..exn]], Sequence[s2, t, [AppendAfterStop, ..exn]]]
Sequence[AppendAfter[s1, s2], t, [AppendAfterStop, ..exn]]:
forEach(
self: AppendAfter[s1, s2],
consumer: Fn(t) / [AppendAfterStop, ..exn]
) / [AppendAfterStop, ..exn]:
match try({
self.seq1.forEach(fn(i: t) / [AppendAfterStop, ..exn] {
# Weird way to update `self.amt`, but this is needed to demonstrate that issue with
# outer `AppendAfter`'s exception being caught by the inner `AppendAfter`.
let amt = self.amt
self.amt -= 1
if amt == 0:
throw(~AppendAfterStop)
consumer(i)
})
}):
Result.Ok(()) | Result.Err(~AppendAfterStop):
self.seq2.forEach(consumer)
# ------------------------------------------------------------------------------
type EmptySeq:
EmptySeq
impl Sequence[EmptySeq, t, exn]:
forEach(self: EmptySeq, consumer: Fn(t) / exn) / exn:
()
# ------------------------------------------------------------------------------
main():
let seq =
AppendAfter(
seq1 = AppendAfter(seq1 = CountFrom(from = 0), seq2 = CountFrom(from = 20), amt = 10),
seq2 = EmptySeq.EmptySeq,
amt = 5,
)
try[[AppendAfterStop], (), []]({
seq.forEach(fn(i: U32) {
print(i)
})
})
()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment