koatl 0.1.29__tar.gz → 0.1.30__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {koatl-0.1.29 → koatl-0.1.30}/Cargo.lock +1 -1
- {koatl-0.1.29 → koatl-0.1.30}/PKG-INFO +1 -1
- {koatl-0.1.29 → koatl-0.1.30}/koatl/Cargo.toml +1 -1
- koatl-0.1.30/koatl/python/koatl/prelude/__init__.tl +4 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/prelude/functional/algebra.tl +28 -8
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/prelude/functional/async.tl +9 -3
- {koatl-0.1.29 → koatl-0.1.30/koatl}/python/koatl/prelude/functional/list.tl +6 -2
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/prelude/functional/memo.tl +1 -1
- {koatl-0.1.29 → koatl-0.1.30/koatl}/python/koatl/prelude/functional/reader.tl +2 -3
- {koatl-0.1.29 → koatl-0.1.30/koatl}/python/koatl/prelude/functional/result.tl +31 -11
- koatl-0.1.30/koatl/python/koatl/prelude/io.tl +5 -0
- koatl-0.1.30/koatl/python/koatl/prelude/iterable.tl +194 -0
- {koatl-0.1.29 → koatl-0.1.30/koatl}/python/koatl/runtime/__init__.py +1 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/runtime/helpers.py +11 -7
- {koatl-0.1.29 → koatl-0.1.30/koatl}/python/koatl/runtime/record.py +7 -6
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/runtime/virtual.py +3 -3
- {koatl-0.1.29 → koatl-0.1.30}/koatl/src/emit_py.rs +17 -13
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/prelude/iterables.tl +10 -0
- koatl-0.1.30/koatl/tests/e2e/prelude/list.tl +47 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/prelude/memo.tl +1 -1
- koatl-0.1.30/koatl/tests/e2e/prelude/result.tl +99 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/resolve_scopes.rs +4 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/transform.rs +2 -1
- koatl-0.1.30/python/koatl/prelude/__init__.tl +4 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/prelude/functional/algebra.tl +28 -8
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/prelude/functional/async.tl +9 -3
- {koatl-0.1.29/koatl → koatl-0.1.30}/python/koatl/prelude/functional/list.tl +6 -2
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/prelude/functional/memo.tl +1 -1
- {koatl-0.1.29/koatl → koatl-0.1.30}/python/koatl/prelude/functional/reader.tl +2 -3
- {koatl-0.1.29/koatl → koatl-0.1.30}/python/koatl/prelude/functional/result.tl +31 -11
- koatl-0.1.30/python/koatl/prelude/io.tl +5 -0
- koatl-0.1.30/python/koatl/prelude/iterable.tl +194 -0
- {koatl-0.1.29/koatl → koatl-0.1.30}/python/koatl/runtime/__init__.py +1 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/runtime/helpers.py +11 -7
- {koatl-0.1.29/koatl → koatl-0.1.30}/python/koatl/runtime/record.py +7 -6
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/runtime/virtual.py +3 -3
- koatl-0.1.29/koatl/python/koatl/prelude/__init__.tl +0 -2
- koatl-0.1.29/koatl/python/koatl/prelude/iterable.tl +0 -79
- koatl-0.1.29/koatl/tests/e2e/prelude/list.tl +0 -18
- koatl-0.1.29/koatl/tests/e2e/prelude/result.tl +0 -29
- koatl-0.1.29/python/koatl/prelude/__init__.tl +0 -2
- koatl-0.1.29/python/koatl/prelude/iterable.tl +0 -79
- {koatl-0.1.29 → koatl-0.1.30}/Cargo.toml +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/README.md +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/.gitignore +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/LICENSE +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/README.md +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/runtime/classes.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/requirements.txt +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/src/lib.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/coal.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/loops.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/match.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/record.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/base/short_circuit.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/destructure.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/prelude/reader.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/prelude/slice.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/prelude/try.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/util/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/util/module0.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/util/module1.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/e2e/util/module2.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl/tests/test_parse.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/Cargo.toml +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/parser/Cargo.toml +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/parser/src/ast.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/parser/src/lexer.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/parser/src/lib.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/parser/src/parser.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/parser/src/util.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/parser/tests/lexer.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/inference.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/lib.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/main.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/parse_timer.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/parser.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/py/util.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/types.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/koatl-core/src/util.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/pyproject.toml +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/__main__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/cli.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/prelude/functional/__init__.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/runtime/classes.py +0 -0
- {koatl-0.1.29 → koatl-0.1.30}/python/koatl/runtime/meta_finder.py +0 -0
|
@@ -3,7 +3,8 @@ export Monad = class(Trait):
|
|
|
3
3
|
Abstract base class for monads.
|
|
4
4
|
|
|
5
5
|
Subclasses must implement `bind` and `pure`.
|
|
6
|
-
Automatically provides
|
|
6
|
+
Automatically provides (likely suboptimal) standard
|
|
7
|
+
functor operations: map, apply, lift.
|
|
7
8
|
"""
|
|
8
9
|
__slots__ = ()
|
|
9
10
|
|
|
@@ -13,19 +14,20 @@ export Monad = class(Trait):
|
|
|
13
14
|
|
|
14
15
|
# Automatically generated implementations.
|
|
15
16
|
map = (self, f) => self.bind(x => self.pure(f(x)))
|
|
16
|
-
apply = (self,
|
|
17
|
+
apply = (self, mf) => self.bind(x => mf.map(f => f(x)))
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
# TODO: enforce binding only once?
|
|
20
20
|
export MonadOnce = class(Monad, Trait):
|
|
21
21
|
"""
|
|
22
|
-
Abstract base class for
|
|
22
|
+
Abstract base class for monads that don't require branching.
|
|
23
23
|
|
|
24
|
-
A MonadOnce
|
|
24
|
+
A MonadOnce only calls `f` in `bind_once` once, which is suitable for
|
|
25
25
|
use with Python generators that cannot be cloned or reused.
|
|
26
26
|
|
|
27
27
|
Subclasses must implement `bind_once` and `pure`.
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
Subclasses may also implement `bind_gen` for more efficient
|
|
30
|
+
handling of do-blocks.
|
|
29
31
|
"""
|
|
30
32
|
__slots__ = ()
|
|
31
33
|
|
|
@@ -38,5 +40,23 @@ export MonadOnce = class(Monad, Trait):
|
|
|
38
40
|
|
|
39
41
|
# Automatically generated implementations.
|
|
40
42
|
bind = (self, f) => self.bind_once(f)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
export Identity = class(MonadOnce):
|
|
46
|
+
__init__ = (self, *args, **kwargs) => raise ValueError(
|
|
47
|
+
"Identity should not be instantiated directly. "
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
bind_once = (self, f) => f(self)
|
|
51
|
+
|
|
52
|
+
pure = staticmethod& x => x
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
export Traversable = class(Trait):
|
|
56
|
+
"""
|
|
57
|
+
Abstract base class for traversable data structures.
|
|
58
|
+
Subclasses must implement `traverse`.
|
|
59
|
+
"""
|
|
60
|
+
__slots__ = ()
|
|
61
|
+
|
|
62
|
+
traverse = Abstract& (self, f) => None
|
|
@@ -24,7 +24,7 @@ export Async = class(MonadOnce):
|
|
|
24
24
|
if hasattr(result, "__await__"):
|
|
25
25
|
return yield from result.__await__()
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
result
|
|
28
28
|
|
|
29
29
|
bind_gen = (self, gen) => Async.from_generator_fn& () =>
|
|
30
30
|
try:
|
|
@@ -39,5 +39,11 @@ export Async = class(MonadOnce):
|
|
|
39
39
|
|
|
40
40
|
sleep = staticmethod& x => Async(asyncio.sleep(x))
|
|
41
41
|
|
|
42
|
-
#
|
|
43
|
-
|
|
42
|
+
# These execute immediately, so we need to wrap them in a generator
|
|
43
|
+
# (otherwise, there might not be an event loop running)
|
|
44
|
+
|
|
45
|
+
gather = staticmethod& (*args) => Async.from_generator_fn& () =>
|
|
46
|
+
yield from asyncio.gather(*args)
|
|
47
|
+
|
|
48
|
+
from_sync = staticmethod& (f, *args, **kwargs) => Async.from_generator_fn& () =>
|
|
49
|
+
yield from asyncio.get_running_loop().run_in_executor(None, () => f(*args, **kwargs))
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
|
|
3
3
|
import .Monad
|
|
4
|
-
import ..iterable.Iterable
|
|
5
4
|
|
|
6
5
|
export List = class(Monad):
|
|
6
|
+
__new__ = (cls, *args, **kwargs) => raise ValueError(
|
|
7
|
+
"List cannot be instantiated directly. "
|
|
8
|
+
"Use [] or list()."
|
|
9
|
+
)
|
|
10
|
+
|
|
7
11
|
bind_once = (self, f) =>
|
|
8
12
|
raise NotImplementedError(
|
|
9
13
|
"List may not be bound using @ "
|
|
@@ -15,7 +19,7 @@ export List = class(Monad):
|
|
|
15
19
|
|
|
16
20
|
pure = staticmethod& x => [x]
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
len = TraitProperty& self => len(self)
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
for name, method in List.__dict__:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import functools.wraps
|
|
2
|
-
import .algebra.MonadOnce
|
|
2
|
+
import .algebra.(Identity, MonadOnce)
|
|
3
3
|
|
|
4
4
|
export Result = class(MonadOnce):
|
|
5
5
|
__slots__ = ()
|
|
@@ -28,9 +28,7 @@ export Result = class(MonadOnce):
|
|
|
28
28
|
if not self.ok:
|
|
29
29
|
return self
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
bind = bind_once
|
|
31
|
+
f(self.value)
|
|
34
32
|
|
|
35
33
|
bind_gen = (self, gen) =>
|
|
36
34
|
self = Result(self)
|
|
@@ -41,7 +39,7 @@ export Result = class(MonadOnce):
|
|
|
41
39
|
|
|
42
40
|
self = Result(gen.send(self.value))
|
|
43
41
|
except StopIteration(value=value):
|
|
44
|
-
return Result
|
|
42
|
+
return Result(value)
|
|
45
43
|
|
|
46
44
|
pure = staticmethod& x => Ok(x)
|
|
47
45
|
|
|
@@ -74,6 +72,29 @@ export Result = class(MonadOnce):
|
|
|
74
72
|
return Err(f())
|
|
75
73
|
return self
|
|
76
74
|
|
|
75
|
+
T = class(MonadOnce):
|
|
76
|
+
__slots__ = ("value",)
|
|
77
|
+
__init__ = (self, value) =>
|
|
78
|
+
if not hasattr(value, "bind_once"):
|
|
79
|
+
raise TypeError("Expected a MonadOnce value")
|
|
80
|
+
|
|
81
|
+
self.value = value
|
|
82
|
+
|
|
83
|
+
__repr__ = self => f"Result.T({repr(self.value)})"
|
|
84
|
+
|
|
85
|
+
pure = (self, x) => self.value.pure(Result.pure(x))
|
|
86
|
+
|
|
87
|
+
bind_once = (self, f) =>
|
|
88
|
+
self.value.bind_once(x =>
|
|
89
|
+
x = Result(x)
|
|
90
|
+
|
|
91
|
+
if not x.ok:
|
|
92
|
+
return self.value.pure(x)
|
|
93
|
+
|
|
94
|
+
f(x.value)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
77
98
|
export Ok = class(Result):
|
|
78
99
|
__slots__ = ("value",)
|
|
79
100
|
ok = True
|
|
@@ -95,11 +116,12 @@ export Ok = class(Result):
|
|
|
95
116
|
unwrap = self => self.value
|
|
96
117
|
coalesce = (self, f) => self.value
|
|
97
118
|
|
|
119
|
+
|
|
98
120
|
export Err = class(Result):
|
|
99
121
|
__slots__ = ("value",)
|
|
100
122
|
ok = False
|
|
101
|
-
__new__ = (cls, value) => object.__new__(cls)
|
|
102
|
-
__init__ = (self, value) =>
|
|
123
|
+
__new__ = (cls, value=None) => object.__new__(cls)
|
|
124
|
+
__init__ = (self, value=None) =>
|
|
103
125
|
if hasattr(self, "value"):
|
|
104
126
|
return None
|
|
105
127
|
self.value = value
|
|
@@ -109,7 +131,7 @@ export Err = class(Result):
|
|
|
109
131
|
return False
|
|
110
132
|
return self.value == other.value
|
|
111
133
|
|
|
112
|
-
__repr__ = self => f"Err({repr(self.value)})"
|
|
134
|
+
__repr__ = self => f"Err({self.value === None then "" else repr(self.value)})"
|
|
113
135
|
unwrap = self =>
|
|
114
136
|
if self.value matches BaseException():
|
|
115
137
|
raise self.value
|
|
@@ -123,11 +145,9 @@ ExtensionProperty(object, "ok")& _ => True
|
|
|
123
145
|
|
|
124
146
|
ExtensionProperty(object, "result")& Result
|
|
125
147
|
|
|
126
|
-
# Enables @ operator for bare objects.
|
|
127
|
-
ExtensionMethod(object, "bind_gen")& Result.bind_gen
|
|
128
|
-
|
|
129
148
|
__tl__.Ok = Ok
|
|
130
149
|
__tl__.Err = Err
|
|
150
|
+
__tl__.Result = Result
|
|
131
151
|
|
|
132
152
|
__tl__.op_coal = (x, f) =>
|
|
133
153
|
if x matches Result():
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
import builtins
|
|
3
|
+
import .functional.Traversable
|
|
4
|
+
import .functional.(Ok, Err)
|
|
5
|
+
|
|
6
|
+
export Iterable = ExtensionTrait& class(Traversable, Trait):
|
|
7
|
+
iter = Abstract
|
|
8
|
+
|
|
9
|
+
skip = (self, n) =>
|
|
10
|
+
let it = self.iter
|
|
11
|
+
for _ in ..n:
|
|
12
|
+
next(it, None)
|
|
13
|
+
it
|
|
14
|
+
|
|
15
|
+
take = (self, n) =>
|
|
16
|
+
let impl = () =>
|
|
17
|
+
let it = self.iter
|
|
18
|
+
for _ in ..n:
|
|
19
|
+
try:
|
|
20
|
+
yield next(it)
|
|
21
|
+
except StopIteration(value=value):
|
|
22
|
+
return value
|
|
23
|
+
|
|
24
|
+
impl.__name__ = f"take"
|
|
25
|
+
impl.__qualname__ = f"Iterable.take"
|
|
26
|
+
|
|
27
|
+
impl()
|
|
28
|
+
|
|
29
|
+
take_while = (self, f) =>
|
|
30
|
+
let impl = () =>
|
|
31
|
+
for i in self.iter:
|
|
32
|
+
if not f(i):
|
|
33
|
+
return None
|
|
34
|
+
yield i
|
|
35
|
+
|
|
36
|
+
impl.__name__ = f"take_while"
|
|
37
|
+
impl.__qualname__ = f"Iterable.take_while"
|
|
38
|
+
|
|
39
|
+
impl()
|
|
40
|
+
|
|
41
|
+
chain = (self, *others) =>
|
|
42
|
+
itertools.chain(self.iter, *others.map($.iter))
|
|
43
|
+
|
|
44
|
+
zip = (self, *others) =>
|
|
45
|
+
zip(self.iter, *others.map($.iter))
|
|
46
|
+
|
|
47
|
+
enumerate = (self, start=0) =>
|
|
48
|
+
enumerate(self.iter, start)
|
|
49
|
+
|
|
50
|
+
map = (self, f) => builtins.map(f, self.iter)
|
|
51
|
+
|
|
52
|
+
filter = (self, f) => builtins.filter(f, self.iter)
|
|
53
|
+
|
|
54
|
+
flat_map = (self, f) => itertools.chain.from_iterable(self.map(f))
|
|
55
|
+
|
|
56
|
+
reverse = self => reversed(list(self.iter))
|
|
57
|
+
|
|
58
|
+
sort = (self, key=None, reverse=False) =>
|
|
59
|
+
sorted(self.iter, key=key, reverse=reverse)
|
|
60
|
+
|
|
61
|
+
copy = self => itertools.tee(self.iter)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
count = self => len(self.iter)
|
|
65
|
+
|
|
66
|
+
fold = (self, init, f) =>
|
|
67
|
+
let acc = init
|
|
68
|
+
for i in self:
|
|
69
|
+
acc = f(acc, i)
|
|
70
|
+
acc
|
|
71
|
+
|
|
72
|
+
associate = (self, f) =>
|
|
73
|
+
let acc = dict() # Don't use Record here.
|
|
74
|
+
for i in self:
|
|
75
|
+
let value = f(i)
|
|
76
|
+
acc[i] = value
|
|
77
|
+
acc
|
|
78
|
+
|
|
79
|
+
group_by = (self, f) =>
|
|
80
|
+
let acc = dict()
|
|
81
|
+
for i in self:
|
|
82
|
+
let key = f(i)
|
|
83
|
+
if not acc.__contains__(key):
|
|
84
|
+
acc[key] = []
|
|
85
|
+
acc[key].append(i)
|
|
86
|
+
acc
|
|
87
|
+
|
|
88
|
+
count_by = (self, f) =>
|
|
89
|
+
let acc = dict()
|
|
90
|
+
for i in self:
|
|
91
|
+
let key = f(i)
|
|
92
|
+
if not acc.__contains__(key):
|
|
93
|
+
acc[key] = 0
|
|
94
|
+
acc[key] += 1
|
|
95
|
+
acc
|
|
96
|
+
|
|
97
|
+
all = (self, f) =>
|
|
98
|
+
for i in self:
|
|
99
|
+
if not f(i):
|
|
100
|
+
return False
|
|
101
|
+
return True
|
|
102
|
+
|
|
103
|
+
any = (self, f) =>
|
|
104
|
+
for i in self:
|
|
105
|
+
if f(i):
|
|
106
|
+
return True
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
find = (self, f) =>
|
|
110
|
+
for i in self:
|
|
111
|
+
if f(i):
|
|
112
|
+
return Ok(i)
|
|
113
|
+
return Err()
|
|
114
|
+
|
|
115
|
+
first = self =>
|
|
116
|
+
(try next(self.iter)).map_err(_ => None)
|
|
117
|
+
|
|
118
|
+
last = (self, f) =>
|
|
119
|
+
let result = Err()
|
|
120
|
+
for i in self:
|
|
121
|
+
if f(i):
|
|
122
|
+
result = Ok(i)
|
|
123
|
+
return result
|
|
124
|
+
|
|
125
|
+
at = (self, index) =>
|
|
126
|
+
for i in self:
|
|
127
|
+
if i == index:
|
|
128
|
+
return Ok(i)
|
|
129
|
+
return Err()
|
|
130
|
+
|
|
131
|
+
sum = self =>
|
|
132
|
+
let acc = 0
|
|
133
|
+
for i in self:
|
|
134
|
+
acc = acc + i
|
|
135
|
+
acc
|
|
136
|
+
|
|
137
|
+
for_each = (self, f) =>
|
|
138
|
+
for i in self:
|
|
139
|
+
f(i)
|
|
140
|
+
|
|
141
|
+
list = self =>
|
|
142
|
+
list(self.iter)
|
|
143
|
+
|
|
144
|
+
dict = self =>
|
|
145
|
+
dict(self.iter)
|
|
146
|
+
|
|
147
|
+
record = self =>
|
|
148
|
+
Record(self.iter)
|
|
149
|
+
|
|
150
|
+
traverse = (self, f=None) =>
|
|
151
|
+
import .functional.(Async, Result)
|
|
152
|
+
|
|
153
|
+
if f === None:
|
|
154
|
+
f = x => x
|
|
155
|
+
|
|
156
|
+
let it = self.iter
|
|
157
|
+
let v
|
|
158
|
+
try:
|
|
159
|
+
v = next(it)
|
|
160
|
+
except StopIteration():
|
|
161
|
+
return []
|
|
162
|
+
|
|
163
|
+
let m = f(v)
|
|
164
|
+
|
|
165
|
+
# TODO: can special case traversals be generalized?
|
|
166
|
+
|
|
167
|
+
# The Result traversal of a list is short-circuiting.
|
|
168
|
+
# Fall back to Result semantics for bare types.
|
|
169
|
+
if m matches Result() or not hasattr(m, "apply"):
|
|
170
|
+
if not m.ok:
|
|
171
|
+
return m
|
|
172
|
+
|
|
173
|
+
let acc = [m]
|
|
174
|
+
for v in it:
|
|
175
|
+
let fv = f(v)
|
|
176
|
+
if not fv.ok:
|
|
177
|
+
return fv
|
|
178
|
+
acc.append(fv)
|
|
179
|
+
return acc
|
|
180
|
+
|
|
181
|
+
# The Async traversal of a list is gather.
|
|
182
|
+
if m matches Async():
|
|
183
|
+
let acc = [m]
|
|
184
|
+
for v in it:
|
|
185
|
+
acc.append(f(v))
|
|
186
|
+
return Async.gather(*acc)
|
|
187
|
+
|
|
188
|
+
# Fall back to default applicative semantics.
|
|
189
|
+
let acc = m.map([$])
|
|
190
|
+
for v in it:
|
|
191
|
+
m = f(v)
|
|
192
|
+
# acc = liftA2((list, value) => [*list, value])(acc, f(v))
|
|
193
|
+
acc = acc.apply(m.map(x => acc => [*acc, x]))
|
|
194
|
+
acc
|
|
@@ -48,6 +48,7 @@ __tl__ = SimpleNamespace(
|
|
|
48
48
|
op_coal=("??"),
|
|
49
49
|
Ok=dummy("try-expr"),
|
|
50
50
|
Err=dummy("try-expr"),
|
|
51
|
+
Result=dummy("do"),
|
|
51
52
|
**{name: helpers.__dict__[name] for name in helpers.__all__},
|
|
52
53
|
**{name: record.__dict__[name] for name in record.__all__},
|
|
53
54
|
**{name: virtual.__dict__[name] for name in virtual.__all__},
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from functools import wraps
|
|
2
2
|
from .virtual import vget
|
|
3
|
-
from .record import Record
|
|
4
3
|
|
|
5
4
|
|
|
6
5
|
__all__ = ["MatchError"]
|
|
@@ -56,11 +55,10 @@ def do(f):
|
|
|
56
55
|
try:
|
|
57
56
|
m = gen.send(None)
|
|
58
57
|
except StopIteration as e:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return e.value
|
|
58
|
+
raise ValueError(
|
|
59
|
+
"Returning before `@` is not allowed. "
|
|
60
|
+
"Use `return @MonadType.pure(value)` instead."
|
|
61
|
+
) from None
|
|
64
62
|
|
|
65
63
|
def recurse(v):
|
|
66
64
|
nonlocal m
|
|
@@ -76,7 +74,13 @@ def do(f):
|
|
|
76
74
|
|
|
77
75
|
bind_gen = vget(m, "bind_gen")
|
|
78
76
|
except AttributeError:
|
|
79
|
-
|
|
77
|
+
try:
|
|
78
|
+
return vget(m, "bind_once")(recurse)
|
|
79
|
+
except AttributeError:
|
|
80
|
+
# Fallback to Result if m is not a monadic type
|
|
81
|
+
from . import __tl__
|
|
82
|
+
|
|
83
|
+
bind_gen = __tl__.Result(m).bind_gen
|
|
80
84
|
|
|
81
85
|
return bind_gen(gen)
|
|
82
86
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import re
|
|
2
|
+
from .virtual import vget
|
|
2
3
|
|
|
3
4
|
__all__ = ["Record"]
|
|
4
5
|
|
|
@@ -11,9 +12,7 @@ class Record(dict):
|
|
|
11
12
|
try:
|
|
12
13
|
return self[name]
|
|
13
14
|
except KeyError:
|
|
14
|
-
|
|
15
|
-
f"'{type(self).__name__}' object has no attribute '{name}'"
|
|
16
|
-
) from None
|
|
15
|
+
return super().__getattr__(name)
|
|
17
16
|
|
|
18
17
|
def _repr_with_visited(self, visited):
|
|
19
18
|
# Handle cycles by checking if this object is already being processed
|
|
@@ -46,9 +45,11 @@ class Record(dict):
|
|
|
46
45
|
visited.remove(obj_id)
|
|
47
46
|
|
|
48
47
|
def _format_key(self, key):
|
|
49
|
-
if isinstance(key, str)
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
if isinstance(key, str):
|
|
49
|
+
if self._is_identifier(key):
|
|
50
|
+
# If key is an identifier, drop the quotes
|
|
51
|
+
return key
|
|
52
|
+
return f'"{key}"'
|
|
52
53
|
|
|
53
54
|
elif isinstance(key, (int, float, bool, type(None))):
|
|
54
55
|
# If key is a literal like 0, 1, True, False, None, use repr
|
|
@@ -14,12 +14,12 @@ def vget(obj, name, ignore_traits=False):
|
|
|
14
14
|
start = obj.start if obj.start is not None else 0
|
|
15
15
|
step = obj.step if obj.step is not None else 1
|
|
16
16
|
if obj.stop is None:
|
|
17
|
-
return count(start, step)
|
|
17
|
+
return iter(count(start, step))
|
|
18
18
|
else:
|
|
19
|
-
return range(start, obj.stop, step)
|
|
19
|
+
return iter(range(start, obj.stop, step))
|
|
20
20
|
|
|
21
21
|
try:
|
|
22
|
-
return obj.items()
|
|
22
|
+
return iter(obj.items())
|
|
23
23
|
except AttributeError:
|
|
24
24
|
pass
|
|
25
25
|
|
|
@@ -496,19 +496,23 @@ trait PyLiteralExt<'src> {
|
|
|
496
496
|
impl<'src> PyLiteralExt<'src> for PyLiteral<'src> {
|
|
497
497
|
fn emit_py<'py>(&self, ctx: &PyCtx<'py, 'src>, span: &Span) -> PyTlResult<PyObject> {
|
|
498
498
|
Ok(match self {
|
|
499
|
-
PyLiteral::Num(num) =>
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
499
|
+
PyLiteral::Num(num) => {
|
|
500
|
+
let num = num.replace('_', "");
|
|
501
|
+
|
|
502
|
+
match num.parse::<i128>() {
|
|
503
|
+
Ok(i) => ctx.ast_node("Constant", (i,), span)?,
|
|
504
|
+
Err(_) => match num.parse::<f64>() {
|
|
505
|
+
Ok(f) => ctx.ast_node("Constant", (f,), span)?,
|
|
506
|
+
Err(_) => {
|
|
507
|
+
return Err(PyTlErr {
|
|
508
|
+
message: format!("Invalid number literal: {}", num),
|
|
509
|
+
py_err: None,
|
|
510
|
+
span: Some(*span),
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
}
|
|
515
|
+
}
|
|
512
516
|
PyLiteral::Bool(b) => ctx.ast_node("Constant", (b,), span)?,
|
|
513
517
|
PyLiteral::Str(s) => ctx.ast_node("Constant", (s,), span)?,
|
|
514
518
|
PyLiteral::None => ctx.ast_node("Constant", (ctx.py.None(),), span)?,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import util.assert_eq
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
# Async traversal must be concurrent.
|
|
5
|
+
|
|
6
|
+
now = time.time()
|
|
7
|
+
|
|
8
|
+
assert_eq(
|
|
9
|
+
(..10).traverse(x =>
|
|
10
|
+
@Async.sleep(0.1)
|
|
11
|
+
x
|
|
12
|
+
).run()
|
|
13
|
+
(..10).list()
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
assert_eq(time.time() - now < 0.3, True)
|
|
17
|
+
|
|
18
|
+
# Result traversal must short-circuit.
|
|
19
|
+
|
|
20
|
+
# Bare Result traversal should not be wrapped in Ok.
|
|
21
|
+
|
|
22
|
+
assert_eq(
|
|
23
|
+
[1, 2, 3].traverse(x => x + 1)
|
|
24
|
+
[2, 3, 4]
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
counts = 0
|
|
28
|
+
assert_eq(
|
|
29
|
+
(..20).traverse(x =>
|
|
30
|
+
counts += 1
|
|
31
|
+
if x == 10:
|
|
32
|
+
return Err()
|
|
33
|
+
Ok(x)
|
|
34
|
+
)
|
|
35
|
+
Err()
|
|
36
|
+
)
|
|
37
|
+
assert_eq(counts, 11)
|
|
38
|
+
|
|
39
|
+
counts = 0
|
|
40
|
+
assert_eq(
|
|
41
|
+
(..10).traverse(x =>
|
|
42
|
+
counts += 1
|
|
43
|
+
Ok(x)
|
|
44
|
+
)
|
|
45
|
+
(..10).map(Ok).list()
|
|
46
|
+
)
|
|
47
|
+
assert_eq(counts, 10)
|