Skip to content

Commit 5a579b4

Browse files
authored
test defstream (#295)
1 parent d17e443 commit 5a579b4

2 files changed

Lines changed: 92 additions & 3 deletions

File tree

effectful/ops/syntax.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import random
66
import types
77
import typing
8-
from collections.abc import Callable
8+
from collections.abc import Callable, Iterable, Mapping
99
from typing import Annotated, Concatenate, Generic, TypeVar
1010

1111
import tree
@@ -1108,6 +1108,39 @@ def trace(value: Callable[P, T]) -> Callable[P, T]:
11081108
return deffn(body, *bound_sig.args, **bound_sig.kwargs)
11091109

11101110

1111+
@defop
1112+
def defstream(
1113+
body: Annotated[T, Scoped[A | B]],
1114+
streams: Annotated[Mapping[Operation[[], S], Iterable[S]], Scoped[B]],
1115+
) -> Annotated[Iterable[T], Scoped[A]]:
1116+
"""A higher-order operation that represents a for-expression."""
1117+
raise NotImplementedError
1118+
1119+
1120+
@defdata.register(collections.abc.Iterable)
1121+
class _IterableTerm(Generic[T], _BaseTerm[collections.abc.Iterable[T]]):
1122+
@defop
1123+
def __iter__(self: collections.abc.Iterable[T]) -> collections.abc.Iterator[T]:
1124+
if not isinstance(self, Term):
1125+
return iter(self)
1126+
else:
1127+
raise NotImplementedError
1128+
1129+
1130+
@defdata.register(collections.abc.Iterator)
1131+
class _IteratorTerm(Generic[T], _IterableTerm[T]):
1132+
@defop
1133+
def __next__(self: collections.abc.Iterator[T]) -> T:
1134+
if not isinstance(self, Term):
1135+
return next(self)
1136+
else:
1137+
raise NotImplementedError
1138+
1139+
1140+
iter_ = _IterableTerm.__iter__
1141+
next_ = _IteratorTerm.__next__
1142+
1143+
11111144
def syntactic_eq(x: Expr[T], other: Expr[T]) -> bool:
11121145
"""Syntactic equality, ignoring the interpretation of the terms.
11131146

tests/test_ops_syntax.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import functools
22
import inspect
3-
from collections.abc import Callable, Mapping
3+
from collections.abc import Callable, Iterable, Iterator, Mapping
44
from typing import Annotated, ClassVar, TypeVar
55

66
import pytest
77

88
import effectful.handlers.numbers # noqa: F401
9-
from effectful.ops.semantics import call, evaluate, fvsof, handler
9+
from effectful.ops.semantics import call, evaluate, fvsof, handler, typeof
1010
from effectful.ops.syntax import (
1111
Scoped,
1212
_CustomSingleDispatchCallable,
1313
deffn,
1414
defop,
15+
defstream,
1516
defterm,
17+
iter_,
18+
next_,
1619
)
1720
from effectful.ops.types import Operation, Term
1821

@@ -484,3 +487,56 @@ def _(self, x: bool) -> bool:
484487
# Test that the method can be called with a handler
485488
with handler({MyClass.my_singledispatch: lambda self, x: x + 6}):
486489
assert instance.my_singledispatch(5) == 11
490+
491+
492+
def test_defdata_iterable():
493+
@defop
494+
def cons_iterable(*args: int) -> Iterable[int]:
495+
raise NotImplementedError
496+
497+
tm = cons_iterable(1, 2, 3)
498+
assert isinstance(tm, Term)
499+
assert isinstance(tm, Iterable)
500+
assert issubclass(typeof(tm), Iterable)
501+
assert tm.op is cons_iterable
502+
assert tm.args == (1, 2, 3)
503+
504+
tm_iter = iter(tm)
505+
assert isinstance(tm_iter, Term)
506+
assert isinstance(tm_iter, Iterator)
507+
assert issubclass(typeof(tm_iter), Iterator)
508+
assert tm_iter.op is iter_
509+
510+
tm_iter_next = next(tm_iter)
511+
assert isinstance(tm_iter_next, Term)
512+
# assert isinstance(tm_iter_next, numbers.Number) # TODO
513+
# assert issubclass(typeof(tm_iter_next), numbers.Number)
514+
assert tm_iter_next.op is next_
515+
516+
assert list(tm.args) == [1, 2, 3]
517+
518+
519+
def test_defstream_1():
520+
x = defop(int, name="x")
521+
y = defop(int, name="y")
522+
tm = defstream(x() + y(), {x: [1, 2, 3], y: [x() + 1, x() + 2, x() + 3]})
523+
524+
assert isinstance(tm, Term)
525+
assert isinstance(tm, Iterable)
526+
assert issubclass(typeof(tm), Iterable)
527+
assert tm.op is defstream
528+
529+
assert x not in fvsof(tm)
530+
assert y not in fvsof(tm)
531+
532+
tm_iter = iter(tm)
533+
assert isinstance(tm_iter, Term)
534+
assert isinstance(tm_iter, Iterator)
535+
assert issubclass(typeof(tm_iter), Iterator)
536+
assert tm_iter.op is iter_
537+
538+
tm_iter_next = next(tm_iter)
539+
assert isinstance(tm_iter_next, Term)
540+
# assert isinstance(tm_iter_next, numbers.Number) # TODO
541+
# assert issubclass(typeof(tm_iter_next), numbers.Number)
542+
assert tm_iter_next.op is next_

0 commit comments

Comments
 (0)