koatl 0.1.29__tar.gz → 0.1.31__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.31}/Cargo.lock +1 -1
- {koatl-0.1.29 → koatl-0.1.31}/PKG-INFO +1 -1
- {koatl-0.1.29 → koatl-0.1.31}/koatl/Cargo.toml +1 -1
- koatl-0.1.31/koatl/python/koatl/prelude/__init__.tl +4 -0
- koatl-0.1.31/koatl/python/koatl/prelude/functional/__init__.tl +26 -0
- koatl-0.1.31/koatl/python/koatl/prelude/functional/algebra.tl +72 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/prelude/functional/async.tl +9 -3
- koatl-0.1.31/koatl/python/koatl/prelude/functional/env.tl +42 -0
- {koatl-0.1.29 → koatl-0.1.31/koatl}/python/koatl/prelude/functional/list.tl +6 -2
- koatl-0.1.31/koatl/python/koatl/prelude/functional/memo.tl +121 -0
- {koatl-0.1.29 → koatl-0.1.31/koatl}/python/koatl/prelude/functional/result.tl +33 -13
- koatl-0.1.31/koatl/python/koatl/prelude/io.tl +5 -0
- koatl-0.1.31/koatl/python/koatl/prelude/iterable.tl +194 -0
- {koatl-0.1.29 → koatl-0.1.31/koatl}/python/koatl/runtime/__init__.py +1 -1
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/runtime/helpers.py +11 -7
- {koatl-0.1.29 → koatl-0.1.31/koatl}/python/koatl/runtime/record.py +7 -6
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/runtime/virtual.py +3 -3
- {koatl-0.1.29 → koatl-0.1.31}/koatl/src/emit_py.rs +17 -13
- koatl-0.1.31/koatl/tests/e2e/prelude/env.tl +9 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/iterables.tl +10 -0
- koatl-0.1.31/koatl/tests/e2e/prelude/list.tl +47 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/memo.tl +30 -1
- koatl-0.1.31/koatl/tests/e2e/prelude/result.tl +99 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/resolve_scopes.rs +4 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/transform.rs +3 -6
- koatl-0.1.31/python/koatl/prelude/__init__.tl +4 -0
- koatl-0.1.31/python/koatl/prelude/functional/__init__.tl +26 -0
- koatl-0.1.31/python/koatl/prelude/functional/algebra.tl +72 -0
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/prelude/functional/async.tl +9 -3
- koatl-0.1.31/python/koatl/prelude/functional/env.tl +42 -0
- {koatl-0.1.29/koatl → koatl-0.1.31}/python/koatl/prelude/functional/list.tl +6 -2
- koatl-0.1.31/python/koatl/prelude/functional/memo.tl +121 -0
- {koatl-0.1.29/koatl → koatl-0.1.31}/python/koatl/prelude/functional/result.tl +33 -13
- koatl-0.1.31/python/koatl/prelude/io.tl +5 -0
- koatl-0.1.31/python/koatl/prelude/iterable.tl +194 -0
- {koatl-0.1.29/koatl → koatl-0.1.31}/python/koatl/runtime/__init__.py +1 -1
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/runtime/helpers.py +11 -7
- {koatl-0.1.29/koatl → koatl-0.1.31}/python/koatl/runtime/record.py +7 -6
- {koatl-0.1.29 → koatl-0.1.31}/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/functional/__init__.tl +0 -37
- koatl-0.1.29/koatl/python/koatl/prelude/functional/algebra.tl +0 -42
- koatl-0.1.29/koatl/python/koatl/prelude/functional/memo.tl +0 -83
- koatl-0.1.29/koatl/python/koatl/prelude/functional/reader.tl +0 -31
- 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/reader.tl +0 -11
- 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/functional/__init__.tl +0 -37
- koatl-0.1.29/python/koatl/prelude/functional/algebra.tl +0 -42
- koatl-0.1.29/python/koatl/prelude/functional/memo.tl +0 -83
- koatl-0.1.29/python/koatl/prelude/functional/reader.tl +0 -31
- koatl-0.1.29/python/koatl/prelude/iterable.tl +0 -79
- {koatl-0.1.29 → koatl-0.1.31}/Cargo.toml +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/README.md +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/.gitignore +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/LICENSE +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/README.md +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/runtime/classes.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/requirements.txt +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/src/lib.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/coal.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/loops.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/match.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/record.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/short_circuit.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/destructure.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/slice.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/try.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/util/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/util/module0.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/util/module1.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/util/module2.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/test_parse.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/Cargo.toml +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/Cargo.toml +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/ast.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/lexer.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/lib.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/parser.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/util.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/tests/lexer.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/inference.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/lib.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/main.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/parse_timer.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/parser.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/py/util.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/types.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/util.rs +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/pyproject.toml +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/__main__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/cli.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/runtime/classes.py +0 -0
- {koatl-0.1.29 → koatl-0.1.31}/python/koatl/runtime/meta_finder.py +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export import .algebra.*
|
|
2
|
+
export import .result.*
|
|
3
|
+
export import .async.*
|
|
4
|
+
export import .env.*
|
|
5
|
+
export import .memo.*
|
|
6
|
+
export import .list.*
|
|
7
|
+
|
|
8
|
+
export id = x => x
|
|
9
|
+
|
|
10
|
+
export compose = (*args) =>
|
|
11
|
+
args match:
|
|
12
|
+
[] => raise ValueError("At least one function is required for composition")
|
|
13
|
+
[f] => f
|
|
14
|
+
[*fs] =>
|
|
15
|
+
let composed = (*args, **kwargs) =>
|
|
16
|
+
let value = fs[-1](*args, **kwargs)
|
|
17
|
+
for f in fs[..-1..-1]:
|
|
18
|
+
value = f(value)
|
|
19
|
+
value
|
|
20
|
+
|
|
21
|
+
composed.__name__ = "<Fn.compose()>"
|
|
22
|
+
composed.__qualname__ = composed.__name__
|
|
23
|
+
composed.signature = fs[-1].signature
|
|
24
|
+
|
|
25
|
+
composed
|
|
26
|
+
default raise ValueError("Invalid arguments for Fn.compose()")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export Monad = class(Trait):
|
|
2
|
+
"""
|
|
3
|
+
Abstract base class for monads.
|
|
4
|
+
|
|
5
|
+
Subclasses must implement `bind` and `pure`.
|
|
6
|
+
Automatically provides (likely suboptimal) standard
|
|
7
|
+
functor operations: map, apply, lift.
|
|
8
|
+
"""
|
|
9
|
+
__slots__ = ()
|
|
10
|
+
|
|
11
|
+
bind = Abstract& (self, f) => None
|
|
12
|
+
|
|
13
|
+
pure = staticmethod& Abstract& value => None
|
|
14
|
+
|
|
15
|
+
# Automatically generated implementations.
|
|
16
|
+
map = (self, f) => self.bind(x => self.pure(f(x)))
|
|
17
|
+
apply = (self, mf) => self.bind(x => mf.map(f => f(x)))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export MonadOnce = class(Monad, Trait):
|
|
21
|
+
"""
|
|
22
|
+
Abstract base class for monads that don't require branching.
|
|
23
|
+
In other words, a deterministic monad.
|
|
24
|
+
|
|
25
|
+
A MonadOnce only calls `f` in `bind_once` once, which is suitable for
|
|
26
|
+
use with Python generators that cannot be cloned or reused.
|
|
27
|
+
|
|
28
|
+
Subclasses must implement `bind_once` and `pure`.
|
|
29
|
+
|
|
30
|
+
Subclasses may also implement `bind_gen` for more efficient
|
|
31
|
+
handling of do-blocks.
|
|
32
|
+
"""
|
|
33
|
+
__slots__ = ()
|
|
34
|
+
|
|
35
|
+
# The default implementation required for `@` syntax that should be overridden by subclasses.
|
|
36
|
+
bind_once = Abstract& (self, f) => None
|
|
37
|
+
|
|
38
|
+
# An optional, optimized implementation of `bind` that skips deep recursion.
|
|
39
|
+
# TODO: can this be automatically generated from bind_once?
|
|
40
|
+
# bind_gen = (self, gen) => ...
|
|
41
|
+
|
|
42
|
+
# Automatically generated implementations.
|
|
43
|
+
bind = (self, f) => self.bind_once(f)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
export Identity = class(MonadOnce):
|
|
47
|
+
"""
|
|
48
|
+
A utility class for preventing `@` from wrapping the function output.
|
|
49
|
+
|
|
50
|
+
The semantics are unusual:
|
|
51
|
+
- pure returns the value directly, NOT wrapped in Identity.
|
|
52
|
+
- bind_once can be called on either Identity(value) or a bare value directly.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
__slots__ = ("value",)
|
|
56
|
+
__match_args__ = ("value",)
|
|
57
|
+
|
|
58
|
+
__init__ = (self, value) => self.value = value
|
|
59
|
+
|
|
60
|
+
bind_once = (self, f) => self matches Identity(value) then f(value) else f(self)
|
|
61
|
+
|
|
62
|
+
pure = staticmethod& x => x
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
export Traversable = class(Trait):
|
|
66
|
+
"""
|
|
67
|
+
Abstract base class for traversable data structures.
|
|
68
|
+
Subclasses must implement `traverse`.
|
|
69
|
+
"""
|
|
70
|
+
__slots__ = ()
|
|
71
|
+
|
|
72
|
+
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))
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import functools.wraps
|
|
2
|
+
import .MonadOnce
|
|
3
|
+
|
|
4
|
+
export Env = class(MonadOnce):
|
|
5
|
+
__slots__ = ("f",)
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
A monad that provides a *mutable* reference to the environment.
|
|
9
|
+
This means that it can behave like a State, without needing to
|
|
10
|
+
explicitly write updates to the context.
|
|
11
|
+
|
|
12
|
+
Structurally, it is identical to Reader.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
__init__ = (self, f) => self.f = f
|
|
16
|
+
|
|
17
|
+
__repr__ = self => "Env(...)"
|
|
18
|
+
|
|
19
|
+
run = (self, ctx) => self.f(ctx)
|
|
20
|
+
|
|
21
|
+
pure = staticmethod& value => Env(ctx => value)
|
|
22
|
+
|
|
23
|
+
get = None # to be populated later
|
|
24
|
+
|
|
25
|
+
item = staticmethod& name => Env(ctx => ctx[name])
|
|
26
|
+
|
|
27
|
+
bind_once = (self, f) => Env& ctx =>
|
|
28
|
+
let v = f(self.f(ctx))
|
|
29
|
+
if v matches Env():
|
|
30
|
+
return v.f(ctx)
|
|
31
|
+
v
|
|
32
|
+
|
|
33
|
+
bind_gen = (self, gen) => Env& ctx =>
|
|
34
|
+
try:
|
|
35
|
+
while True:
|
|
36
|
+
self = gen.send(self.f(ctx))
|
|
37
|
+
except StopIteration(value=value):
|
|
38
|
+
return value
|
|
39
|
+
|
|
40
|
+
NoKey = object()
|
|
41
|
+
|
|
42
|
+
Env.get = Env(ctx => ctx)
|
|
@@ -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__:
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import collections.defaultdict
|
|
2
|
+
import functools.wraps
|
|
3
|
+
|
|
4
|
+
import .algebra.(MonadOnce, Identity)
|
|
5
|
+
import .result.(Result, Ok, Err)
|
|
6
|
+
import .env.Env
|
|
7
|
+
|
|
8
|
+
export Memo = class(Env):
|
|
9
|
+
__slots__ = ("f",)
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
The Memo monad is a specialization of Env, where
|
|
13
|
+
the environment is a cache that stores previously computed values.
|
|
14
|
+
It is used to memoize computations, allowing for efficient reuse of results
|
|
15
|
+
without recomputing them.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
Cache = class:
|
|
19
|
+
"""
|
|
20
|
+
Basic implementation of a cache without any eviction policy.
|
|
21
|
+
"""
|
|
22
|
+
__init__ = self =>
|
|
23
|
+
self.cache = defaultdict(dict)
|
|
24
|
+
|
|
25
|
+
__repr__ = self => f"Memo.Cache({self.cache})"
|
|
26
|
+
|
|
27
|
+
try_get = (self, name, deps) => try self.cache[name][deps] except KeyError()
|
|
28
|
+
|
|
29
|
+
update = (self, name, deps, value) =>
|
|
30
|
+
self.cache[name][deps] = value
|
|
31
|
+
value
|
|
32
|
+
|
|
33
|
+
NullCache = class:
|
|
34
|
+
"""
|
|
35
|
+
A cache that does not store any values.
|
|
36
|
+
Useful for testing or when memoization is not needed.
|
|
37
|
+
"""
|
|
38
|
+
__init__ = self => None
|
|
39
|
+
|
|
40
|
+
__repr__ = self => "Memo.NullCache()"
|
|
41
|
+
|
|
42
|
+
try_get = (self, name, deps) => Err()
|
|
43
|
+
|
|
44
|
+
update = (self, name, deps, value) => value
|
|
45
|
+
|
|
46
|
+
__init__ = (self, f) => self.f = f
|
|
47
|
+
|
|
48
|
+
__repr__ = self => f"Memo(...)"
|
|
49
|
+
|
|
50
|
+
# We need to handle
|
|
51
|
+
# memo @value
|
|
52
|
+
# separately, because we *don't* want to memoize the monad itself
|
|
53
|
+
# but rather the value it produces.
|
|
54
|
+
# Another way to think about it is that
|
|
55
|
+
# fused_value produces a Memo.T<MonadOnce<T>> value.
|
|
56
|
+
value = staticmethod& (id, deps, f) =>
|
|
57
|
+
Memo(ctx =>
|
|
58
|
+
if ctx.try_get(id, tuple(deps)) matches Ok((inner_type, value)):
|
|
59
|
+
if inner_type === None or inner_type === Memo:
|
|
60
|
+
# Non-monadic cached values, or Memo cached values, shouldn't
|
|
61
|
+
# wrap the output in a monadic type.
|
|
62
|
+
|
|
63
|
+
# The issue is that the present do-block expects a value to be bound,
|
|
64
|
+
# but we don't want to use @Memo.pure(value) because that would
|
|
65
|
+
# produce a Memo<Memo<T>> value.
|
|
66
|
+
# It's hacky, but it works for now.
|
|
67
|
+
return @Identity(value)
|
|
68
|
+
else:
|
|
69
|
+
# This gives us a Memo<MonadOnce<T>> value as normal.
|
|
70
|
+
return @inner_type.pure(value)
|
|
71
|
+
|
|
72
|
+
let inner = f()
|
|
73
|
+
let inner_type = type(inner)
|
|
74
|
+
|
|
75
|
+
if isinstance(inner, Memo):
|
|
76
|
+
inner = @Identity(inner.f(ctx))
|
|
77
|
+
else if not hasattr(inner, "bind_once"):
|
|
78
|
+
inner_type = None
|
|
79
|
+
inner = @Identity(inner)
|
|
80
|
+
else:
|
|
81
|
+
inner = @inner
|
|
82
|
+
|
|
83
|
+
ctx.update(id, tuple(deps), (inner_type, inner))
|
|
84
|
+
inner
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
fn = staticmethod& f => wraps(f)& (*args, **kwargs) =>
|
|
88
|
+
let id = f"{f.__module__}.{f.__qualname__}"
|
|
89
|
+
let deps = (tuple(args), tuple(kwargs.items()))
|
|
90
|
+
|
|
91
|
+
# Same semantics as Memo.value above.
|
|
92
|
+
Memo.value(id, deps, () => f(*args, **kwargs))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
run = (self, ctx=None) =>
|
|
96
|
+
if ctx === None:
|
|
97
|
+
ctx = Memo.Cache()
|
|
98
|
+
self.f(ctx)
|
|
99
|
+
|
|
100
|
+
pure = staticmethod& value => Memo(ctx => value)
|
|
101
|
+
|
|
102
|
+
bind_once = (self, f) => Memo(ctx =>
|
|
103
|
+
let value = f(self.f(ctx))
|
|
104
|
+
if value matches Memo():
|
|
105
|
+
return value.f(ctx)
|
|
106
|
+
value
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
bind_gen = (self, gen) => Memo(ctx =>
|
|
110
|
+
self = self.f(ctx)
|
|
111
|
+
try:
|
|
112
|
+
while True:
|
|
113
|
+
self = gen.send(self)
|
|
114
|
+
if self matches Memo():
|
|
115
|
+
self = self.f(ctx)
|
|
116
|
+
except StopIteration(value=value):
|
|
117
|
+
return value
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
__tl__.memo_value = Memo.value
|
|
@@ -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
|
|
@@ -81,8 +102,8 @@ export Ok = class(Result):
|
|
|
81
102
|
__init__ = (self, value) =>
|
|
82
103
|
if hasattr(self, "value"):
|
|
83
104
|
# This is necessary to prevent overwriting the value
|
|
84
|
-
# since Python's Result.__new__ will call Ok.__init__ again
|
|
85
|
-
#
|
|
105
|
+
# since Python's Result.__new__ will call Ok.__init__ again,
|
|
106
|
+
# in the case we call Result(Ok(value))
|
|
86
107
|
return None
|
|
87
108
|
self.value = value
|
|
88
109
|
|
|
@@ -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
|
|
@@ -43,11 +43,11 @@ __tl__ = SimpleNamespace(
|
|
|
43
43
|
# These require more complex logic and require the prelude.
|
|
44
44
|
# The runtime provides dummy implementations that raise if used without the prelude.
|
|
45
45
|
memo_value=dummy("memo"),
|
|
46
|
-
bind_memo_value=dummy("memo"),
|
|
47
46
|
op_map=dummy("?"),
|
|
48
47
|
op_coal=("??"),
|
|
49
48
|
Ok=dummy("try-expr"),
|
|
50
49
|
Err=dummy("try-expr"),
|
|
50
|
+
Result=dummy("Result"),
|
|
51
51
|
**{name: helpers.__dict__[name] for name in helpers.__all__},
|
|
52
52
|
**{name: record.__dict__[name] for name in record.__all__},
|
|
53
53
|
**{name: virtual.__dict__[name] for name in virtual.__all__},
|