Skip to content

Instantly share code, notes, and snippets.

@osa1
Last active December 5, 2025 19:51
Show Gist options
  • Select an option

  • Save osa1/7506e9a5074dc54f75c09e5767209f86 to your computer and use it in GitHub Desktop.

Select an option

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,
)
type AppendAfterStop
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(
\(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(\(i: U32): print(i)))
()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment