koatl 0.1.31__tar.gz → 0.1.33__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.31 → koatl-0.1.33}/Cargo.lock +1 -1
- {koatl-0.1.31 → koatl-0.1.33}/PKG-INFO +1 -1
- {koatl-0.1.31 → koatl-0.1.33}/koatl/Cargo.toml +1 -1
- {koatl-0.1.31 → koatl-0.1.33/koatl}/python/koatl/prelude/__init__.tl +3 -1
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/async.tl +6 -5
- {koatl-0.1.31 → koatl-0.1.33/koatl}/python/koatl/prelude/functional/env.tl +6 -3
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/list.tl +2 -6
- koatl-0.1.33/koatl/python/koatl/prelude/functional/memo.tl +187 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/result.tl +52 -35
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/iterable.tl +70 -7
- koatl-0.1.33/koatl/python/koatl/prelude/regex.tl +60 -0
- {koatl-0.1.31 → koatl-0.1.33/koatl}/python/koatl/runtime/__init__.py +1 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/runtime/classes.py +23 -3
- {koatl-0.1.31 → koatl-0.1.33/koatl}/python/koatl/runtime/helpers.py +11 -27
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/runtime/record.py +19 -2
- koatl-0.1.33/koatl/python/koatl/runtime/virtual.py +103 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/src/emit_py.rs +18 -2
- koatl-0.1.33/koatl/tests/e2e/base/data.txt +1 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/loops.tl +1 -1
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/match.tl +1 -1
- koatl-0.1.33/koatl/tests/e2e/base/with.tl +6 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/list.tl +2 -2
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/memo.tl +19 -6
- koatl-0.1.33/koatl/tests/e2e/prelude/result.tl +55 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/try.tl +1 -1
- koatl-0.1.33/koatl/tests/e2e/prelude/virtual.tl +32 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/test_e2e.py +2 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/ast.rs +3 -2
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/lexer.rs +58 -10
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/parser.rs +89 -66
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/util.rs +0 -8
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/inference.rs +5 -5
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/py/ast.rs +2 -1
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/py/emit.rs +21 -4
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/py/util.rs +9 -1
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/resolve_scopes.rs +25 -10
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/transform.rs +37 -23
- {koatl-0.1.31/koatl → koatl-0.1.33}/python/koatl/prelude/__init__.tl +3 -1
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/async.tl +6 -5
- {koatl-0.1.31/koatl → koatl-0.1.33}/python/koatl/prelude/functional/env.tl +6 -3
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/list.tl +2 -6
- koatl-0.1.33/python/koatl/prelude/functional/memo.tl +187 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/result.tl +52 -35
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/iterable.tl +70 -7
- koatl-0.1.33/python/koatl/prelude/regex.tl +60 -0
- {koatl-0.1.31/koatl → koatl-0.1.33}/python/koatl/runtime/__init__.py +1 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/runtime/classes.py +23 -3
- {koatl-0.1.31/koatl → koatl-0.1.33}/python/koatl/runtime/helpers.py +11 -27
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/runtime/record.py +19 -2
- koatl-0.1.33/python/koatl/runtime/virtual.py +103 -0
- koatl-0.1.31/koatl/python/koatl/prelude/functional/async_util.py +0 -15
- koatl-0.1.31/koatl/python/koatl/prelude/functional/memo.tl +0 -121
- koatl-0.1.31/koatl/python/koatl/runtime/virtual.py +0 -103
- koatl-0.1.31/koatl/tests/e2e/destructure.tl +0 -0
- koatl-0.1.31/koatl/tests/e2e/prelude/result.tl +0 -99
- koatl-0.1.31/koatl/tests/e2e/prelude/virtual.tl +0 -14
- koatl-0.1.31/koatl-core/parser/tests/lexer.rs +0 -224
- koatl-0.1.31/python/koatl/prelude/functional/async_util.py +0 -15
- koatl-0.1.31/python/koatl/prelude/functional/memo.tl +0 -121
- koatl-0.1.31/python/koatl/runtime/virtual.py +0 -103
- {koatl-0.1.31 → koatl-0.1.33}/Cargo.toml +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/README.md +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/.gitignore +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/LICENSE +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/README.md +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/algebra.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/io.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/requirements.txt +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/src/lib.rs +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/coal.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/record.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/short_circuit.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/env.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/iterables.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/slice.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/util/__init__.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/util/module0.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/util/module1.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/util/module2.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/test_parse.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/Cargo.toml +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/Cargo.toml +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/lib.rs +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/lib.rs +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/main.rs +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/parse_timer.rs +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/parser.rs +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/types.rs +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/util.rs +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/pyproject.toml +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/__init__.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/__main__.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/cli.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/__init__.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/algebra.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/io.tl +0 -0
- {koatl-0.1.31 → koatl-0.1.33}/python/koatl/runtime/meta_finder.py +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import functools.wraps
|
|
2
2
|
import asyncio
|
|
3
3
|
|
|
4
|
-
import .async_util
|
|
5
4
|
import .MonadOnce
|
|
6
5
|
|
|
7
6
|
export Async = class(MonadOnce):
|
|
@@ -16,20 +15,22 @@ export Async = class(MonadOnce):
|
|
|
16
15
|
m.generator = generator_fn(*args, **kwargs)
|
|
17
16
|
return m
|
|
18
17
|
|
|
19
|
-
run = self => asyncio.run(
|
|
18
|
+
run = self => asyncio.run((() => await self)())
|
|
20
19
|
|
|
21
20
|
bind_once = (self, f) => Async.from_generator_fn& () =>
|
|
22
21
|
let result = f(yield from self.__await__())
|
|
23
22
|
|
|
24
|
-
if hasattr(result, "__await__"):
|
|
25
|
-
return
|
|
23
|
+
if not hasattr(result, "__await__"):
|
|
24
|
+
raise ValueError(f"Expected the binder to return an Awaitable, but got {type(result).__name__}")
|
|
26
25
|
|
|
27
|
-
result
|
|
26
|
+
yield from result.__await__()
|
|
28
27
|
|
|
29
28
|
bind_gen = (self, gen) => Async.from_generator_fn& () =>
|
|
30
29
|
try:
|
|
31
30
|
while True:
|
|
32
31
|
self = gen.send(yield from self.__await__())
|
|
32
|
+
if not hasattr(self, "__await__"):
|
|
33
|
+
raise ValueError(f"Expected the binder to return an Awaitable, but got {type(self).__name__}")
|
|
33
34
|
except StopIteration(value=value):
|
|
34
35
|
return value
|
|
35
36
|
|
|
@@ -26,14 +26,17 @@ export Env = class(MonadOnce):
|
|
|
26
26
|
|
|
27
27
|
bind_once = (self, f) => Env& ctx =>
|
|
28
28
|
let v = f(self.f(ctx))
|
|
29
|
-
if v matches Env():
|
|
30
|
-
return v
|
|
31
|
-
|
|
29
|
+
if v matches not Env():
|
|
30
|
+
raise ValueError(f"Expected the binder to return an Env, but got {type(v)}")
|
|
31
|
+
|
|
32
|
+
v.f(ctx)
|
|
32
33
|
|
|
33
34
|
bind_gen = (self, gen) => Env& ctx =>
|
|
34
35
|
try:
|
|
35
36
|
while True:
|
|
36
37
|
self = gen.send(self.f(ctx))
|
|
38
|
+
if self matches not Env():
|
|
39
|
+
raise ValueError(f"Expected the binder to return an Env, but got {type(self)}")
|
|
37
40
|
except StopIteration(value=value):
|
|
38
41
|
return value
|
|
39
42
|
|
|
@@ -19,11 +19,7 @@ export List = class(Monad):
|
|
|
19
19
|
|
|
20
20
|
pure = staticmethod& x => [x]
|
|
21
21
|
|
|
22
|
-
len =
|
|
22
|
+
len = property& self => len(self)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
if name.startswith("_"):
|
|
27
|
-
continue
|
|
28
|
-
|
|
29
|
-
ExtensionMethod(list, name)& method
|
|
25
|
+
Extension.property(list, "len")& List.len.fget
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import collections.defaultdict
|
|
2
|
+
import functools.wraps
|
|
3
|
+
|
|
4
|
+
import .algebra.(MonadOnce, Identity)
|
|
5
|
+
import .result.(Result, Ok, Err)
|
|
6
|
+
import .async.(Async)
|
|
7
|
+
|
|
8
|
+
export Memo = class(MonadOnce):
|
|
9
|
+
__slots__ = ("f",)
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
The Memo monad is like a 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
|
+
value = staticmethod& (id, deps, f) =>
|
|
51
|
+
"""
|
|
52
|
+
Create a memoized value with the given id and dependencies.
|
|
53
|
+
f should be a function taking no arguments that returns the value to be memoized.
|
|
54
|
+
The id is a unique identifier for the memoized value, and deps are the dependencies
|
|
55
|
+
that determine when the value should be recomputed.
|
|
56
|
+
If the value is already cached, it will return the cached value.
|
|
57
|
+
If the value is not cached, it will compute the value using f and cache it.
|
|
58
|
+
"""
|
|
59
|
+
Memo(ctx =>
|
|
60
|
+
if ctx.try_get(id, tuple(deps)) matches Ok(value):
|
|
61
|
+
return value
|
|
62
|
+
|
|
63
|
+
let inner = f()
|
|
64
|
+
let inner_type
|
|
65
|
+
|
|
66
|
+
if isinstance(inner, Memo):
|
|
67
|
+
# Collapse Memo<Memo<T>> into Memo<T>.
|
|
68
|
+
# Usually, this should only happen inside .flat(),
|
|
69
|
+
# but since we don't have type inference,
|
|
70
|
+
# we need to do it here to nesting memo inside memo.
|
|
71
|
+
inner = inner.f(ctx)
|
|
72
|
+
|
|
73
|
+
ctx.update(id, tuple(deps), inner)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
fn = staticmethod& f => wraps(f)& (*args, **kwargs) =>
|
|
77
|
+
let id = f"{f.__module__}.{f.__qualname__}"
|
|
78
|
+
let deps = (tuple(args), tuple(kwargs.items()))
|
|
79
|
+
Memo.value(id, deps, () => f(*args, **kwargs))
|
|
80
|
+
|
|
81
|
+
run = (self, ctx=None) =>
|
|
82
|
+
if ctx === None:
|
|
83
|
+
ctx = Memo.Cache()
|
|
84
|
+
self.f(ctx)
|
|
85
|
+
|
|
86
|
+
pure = staticmethod& value => Memo(ctx => value)
|
|
87
|
+
|
|
88
|
+
bind_once = (self, f) => Memo(ctx =>
|
|
89
|
+
let value = f(self.f(ctx))
|
|
90
|
+
|
|
91
|
+
if value matches not Memo():
|
|
92
|
+
raise ValueError(f"Expected the binder to return a Memo, but got {type(value)}")
|
|
93
|
+
|
|
94
|
+
return value.f(ctx)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
bind_gen = (self, gen) => Memo(ctx =>
|
|
98
|
+
try:
|
|
99
|
+
while True:
|
|
100
|
+
let inner = self.f(ctx)
|
|
101
|
+
self = gen.send(inner)
|
|
102
|
+
|
|
103
|
+
if self matches not Memo():
|
|
104
|
+
raise ValueError(f"Expected the binder to return a Memo, but got {type(value)}")
|
|
105
|
+
|
|
106
|
+
except StopIteration(value=value):
|
|
107
|
+
return value
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
export AsyncMemo = class(MonadOnce):
|
|
113
|
+
"""
|
|
114
|
+
Like Memo, but fuses Async computations with the Memo itself.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
__slots__ = ("f",)
|
|
118
|
+
|
|
119
|
+
__init__ = (self, f) => self.f = f
|
|
120
|
+
|
|
121
|
+
__repr__ = self => f"AsyncMemo(...)"
|
|
122
|
+
|
|
123
|
+
value = staticmethod& (id, deps, f) =>
|
|
124
|
+
"""
|
|
125
|
+
Like Memo.value, but we need to cache the result of the Async.
|
|
126
|
+
"""
|
|
127
|
+
AsyncMemo(ctx =>
|
|
128
|
+
# This pauses the generator to wait for an Async context.
|
|
129
|
+
# This allows with_ctx to work a little bit better with memoization,
|
|
130
|
+
# deferring the cache lookup until we are in an async context.
|
|
131
|
+
@Async.pure(None)
|
|
132
|
+
|
|
133
|
+
if ctx.try_get(id, tuple(deps)) matches Ok(value):
|
|
134
|
+
return value
|
|
135
|
+
|
|
136
|
+
let inner = f()
|
|
137
|
+
let inner_type
|
|
138
|
+
|
|
139
|
+
if isinstance(inner, Memo):
|
|
140
|
+
inner = inner.f(ctx)
|
|
141
|
+
|
|
142
|
+
if not isinstance(inner, Async):
|
|
143
|
+
raise ValueError(f"Expected the inner computation to be Async, but got {type(inner)}")
|
|
144
|
+
|
|
145
|
+
inner = @inner
|
|
146
|
+
|
|
147
|
+
ctx.update(id, tuple(deps), inner)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
fn = staticmethod& f => wraps(f)& (*args, **kwargs) =>
|
|
151
|
+
let id = f"{f.__module__}.{f.__qualname__}"
|
|
152
|
+
let deps = (tuple(args), tuple(kwargs.items()))
|
|
153
|
+
AsyncMemo.value(id, deps, () => f(*args, **kwargs))
|
|
154
|
+
|
|
155
|
+
run = (self, ctx=None) =>
|
|
156
|
+
if ctx === None:
|
|
157
|
+
ctx = Memo.Cache()
|
|
158
|
+
self.f(ctx).run()
|
|
159
|
+
|
|
160
|
+
with_ctx = (self, ctx) => self.f(ctx)
|
|
161
|
+
|
|
162
|
+
pure = staticmethod& value => AsyncMemo(ctx => Async.pure(value))
|
|
163
|
+
|
|
164
|
+
bind_once = (self, f) => AsyncMemo(ctx =>
|
|
165
|
+
let value = f(@self.f(ctx))
|
|
166
|
+
|
|
167
|
+
if value matches not AsyncMemo():
|
|
168
|
+
raise ValueError(f"Expected the binder to return a Memo, but got {type(value)}")
|
|
169
|
+
|
|
170
|
+
return value.f(ctx)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
bind_gen = (self, gen) => AsyncMemo(ctx =>
|
|
174
|
+
try:
|
|
175
|
+
while True:
|
|
176
|
+
let inner = @self.f(ctx)
|
|
177
|
+
self = gen.send(inner)
|
|
178
|
+
|
|
179
|
+
if self matches not AsyncMemo():
|
|
180
|
+
raise ValueError(f"Expected the binder to return a Memo, but got {type(value)}")
|
|
181
|
+
|
|
182
|
+
except StopIteration(value=value):
|
|
183
|
+
return value
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
__tl__.memo_value = Memo.value
|
|
187
|
+
__tl__.async_memo_value = AsyncMemo.value
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import functools.wraps
|
|
2
2
|
import .algebra.(Identity, MonadOnce)
|
|
3
3
|
|
|
4
|
+
infer_ok = x =>
|
|
5
|
+
x match:
|
|
6
|
+
None => False
|
|
7
|
+
BaseException() => False
|
|
8
|
+
default True
|
|
9
|
+
|
|
4
10
|
export Result = class(MonadOnce):
|
|
5
11
|
__slots__ = ()
|
|
6
12
|
__match_args__ = ("value",)
|
|
@@ -8,7 +14,7 @@ export Result = class(MonadOnce):
|
|
|
8
14
|
__new__ = (cls, value) =>
|
|
9
15
|
if isinstance(value, Result):
|
|
10
16
|
return value
|
|
11
|
-
value
|
|
17
|
+
infer_ok(value) then Ok(value) else Err(value)
|
|
12
18
|
|
|
13
19
|
__init__ = (self, *args, **kwargs) =>
|
|
14
20
|
raise ValueError("Result should not be instantiated directly, use Ok or Err")
|
|
@@ -24,29 +30,29 @@ export Result = class(MonadOnce):
|
|
|
24
30
|
return Err(e)
|
|
25
31
|
|
|
26
32
|
bind_once = (self, f) =>
|
|
27
|
-
self = Result(self)
|
|
28
33
|
if not self.ok:
|
|
29
34
|
return self
|
|
30
35
|
|
|
31
|
-
f(self.value)
|
|
36
|
+
let value = f(self.value)
|
|
37
|
+
if value matches not Result():
|
|
38
|
+
raise ValueError(f"Expected the binder to return a Result, but got {type(value)}")
|
|
39
|
+
value
|
|
32
40
|
|
|
33
41
|
bind_gen = (self, gen) =>
|
|
34
|
-
self = Result(self)
|
|
35
42
|
try:
|
|
36
43
|
while True:
|
|
37
44
|
if not self.ok:
|
|
38
45
|
return self
|
|
39
46
|
|
|
40
|
-
self =
|
|
47
|
+
self = gen.send(self.value)
|
|
48
|
+
if self matches not Result():
|
|
49
|
+
raise ValueError(f"Expected the binder to return a Result, but got {type(self)}")
|
|
41
50
|
except StopIteration(value=value):
|
|
42
51
|
return Result(value)
|
|
43
52
|
|
|
44
53
|
pure = staticmethod& x => Ok(x)
|
|
45
54
|
|
|
46
55
|
apply = (self, f) =>
|
|
47
|
-
self = Result(self)
|
|
48
|
-
f = Result(f)
|
|
49
|
-
|
|
50
56
|
if not self.ok:
|
|
51
57
|
return self
|
|
52
58
|
if not f.ok:
|
|
@@ -55,44 +61,60 @@ export Result = class(MonadOnce):
|
|
|
55
61
|
Ok(f(self))
|
|
56
62
|
|
|
57
63
|
map = (self, f) =>
|
|
58
|
-
self = Result(self)
|
|
59
64
|
if self.ok:
|
|
60
65
|
return Ok(f(self.value))
|
|
61
66
|
return self
|
|
62
67
|
|
|
63
68
|
map_err = (self, f) =>
|
|
64
|
-
self = Result(self)
|
|
65
69
|
if not self.ok:
|
|
66
70
|
return Err(f(self.value))
|
|
67
71
|
return self
|
|
68
72
|
|
|
69
73
|
map_none = (self, f) =>
|
|
70
|
-
self = Result(self)
|
|
71
74
|
if not self.ok and self.value === None:
|
|
72
75
|
return Err(f())
|
|
73
76
|
return self
|
|
74
77
|
|
|
75
|
-
T =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
T = (typ) =>
|
|
79
|
+
"""
|
|
80
|
+
TODO: cache the Result.T class to avoid creating it every time?
|
|
81
|
+
"""
|
|
82
|
+
if not isinstance(typ, type):
|
|
83
|
+
raise ValueError(f"Expected a type, but got {type(typ).__name__}")
|
|
84
|
+
|
|
85
|
+
let cls = class(MonadOnce):
|
|
86
|
+
__slots__ = ("value",)
|
|
87
|
+
__init__ = (self, value) =>
|
|
88
|
+
if not isinstance(value, typ):
|
|
89
|
+
raise ValueError(f"Expected a {typ}, but got {type(value).__name__}")
|
|
80
90
|
|
|
81
|
-
|
|
91
|
+
self.value = value
|
|
82
92
|
|
|
83
|
-
|
|
93
|
+
__repr__ = self => f"Result.T({typ.__name__})({repr(self.value)})"
|
|
84
94
|
|
|
85
|
-
|
|
95
|
+
pure = (self, x) => cls(typ.pure(Result.pure(x)))
|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
bind_once = (self, f) =>
|
|
98
|
+
cls(self.value.bind_once(x =>
|
|
99
|
+
if not isinstance(x, Result):
|
|
100
|
+
raise ValueError(f"Expected the inner type to be a Result, but got {type(x).__name__}")
|
|
90
101
|
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
if not x.ok:
|
|
103
|
+
return typ.pure(x)
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
let value = f(x.value)
|
|
106
|
+
if not hasattr(value, "value"):
|
|
107
|
+
raise ValueError(
|
|
108
|
+
f"Expected the binder to return a {cls.__name__}, " +
|
|
109
|
+
f"but got a {type(value).__name__}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
value.value
|
|
113
|
+
))
|
|
114
|
+
|
|
115
|
+
cls.__name__ = f"Result.T({typ.__name__})"
|
|
116
|
+
cls.__qualname__ = f"Result.T({typ.__name__})"
|
|
117
|
+
cls
|
|
96
118
|
|
|
97
119
|
|
|
98
120
|
export Ok = class(Result):
|
|
@@ -135,15 +157,10 @@ export Err = class(Result):
|
|
|
135
157
|
unwrap = self =>
|
|
136
158
|
if self.value matches BaseException():
|
|
137
159
|
raise self.value
|
|
138
|
-
raise ValueError(f"Expected Ok, got {repr(self
|
|
160
|
+
raise ValueError(f"Expected Ok, got {repr(self)}")
|
|
139
161
|
coalesce = (self, f) => f()
|
|
140
162
|
|
|
141
|
-
|
|
142
|
-
ExtensionProperty(type(None), "ok")& _ => False
|
|
143
|
-
ExtensionProperty(BaseException, "ok")& _ => False
|
|
144
|
-
ExtensionProperty(object, "ok")& _ => True
|
|
145
|
-
|
|
146
|
-
ExtensionProperty(object, "result")& Result
|
|
163
|
+
Extension.property(object, "result")& Result
|
|
147
164
|
|
|
148
165
|
__tl__.Ok = Ok
|
|
149
166
|
__tl__.Err = Err
|
|
@@ -153,7 +170,7 @@ __tl__.op_coal = (x, f) =>
|
|
|
153
170
|
if x matches Result():
|
|
154
171
|
return x.coalesce(f)
|
|
155
172
|
|
|
156
|
-
if not x
|
|
173
|
+
if not infer_ok(x):
|
|
157
174
|
return f()
|
|
158
175
|
return x
|
|
159
176
|
|
|
@@ -161,6 +178,6 @@ __tl__.op_map = (x, f) =>
|
|
|
161
178
|
if x matches Result():
|
|
162
179
|
return x.map(f)
|
|
163
180
|
|
|
164
|
-
if x
|
|
181
|
+
if infer_ok(x):
|
|
165
182
|
return f(x)
|
|
166
183
|
return x
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import itertools
|
|
2
2
|
import builtins
|
|
3
|
-
import .functional.Traversable
|
|
4
|
-
import .functional.(Ok, Err)
|
|
3
|
+
import .functional.(Traversable, Ok, Err, Memo, Async, AsyncMemo)
|
|
5
4
|
|
|
6
|
-
export Iterable =
|
|
5
|
+
export Iterable = Extension.trait& class(Traversable, Trait):
|
|
7
6
|
iter = Abstract
|
|
8
7
|
|
|
9
8
|
skip = (self, n) =>
|
|
@@ -128,15 +127,67 @@ export Iterable = ExtensionTrait& class(Traversable, Trait):
|
|
|
128
127
|
return Ok(i)
|
|
129
128
|
return Err()
|
|
130
129
|
|
|
130
|
+
for_each = (self, f) =>
|
|
131
|
+
for i in self:
|
|
132
|
+
f(i)
|
|
133
|
+
|
|
134
|
+
unique = self =>
|
|
135
|
+
let seen = set()
|
|
136
|
+
let impl = () =>
|
|
137
|
+
for i in self:
|
|
138
|
+
if not seen.__contains__(i):
|
|
139
|
+
seen.add(i)
|
|
140
|
+
yield i
|
|
141
|
+
|
|
142
|
+
impl.__name__ = f"unique"
|
|
143
|
+
impl.__qualname__ = f"Iterable.unique"
|
|
144
|
+
|
|
145
|
+
impl()
|
|
146
|
+
|
|
147
|
+
join_str = (self, sep="") =>
|
|
148
|
+
sep.join(self.map(str))
|
|
149
|
+
|
|
131
150
|
sum = self =>
|
|
132
151
|
let acc = 0
|
|
133
152
|
for i in self:
|
|
134
153
|
acc = acc + i
|
|
135
154
|
acc
|
|
136
155
|
|
|
137
|
-
|
|
156
|
+
mean = self =>
|
|
157
|
+
let acc = 0
|
|
158
|
+
let count = 0
|
|
138
159
|
for i in self:
|
|
139
|
-
|
|
160
|
+
acc = acc + i
|
|
161
|
+
count = count + 1
|
|
162
|
+
if count == 0:
|
|
163
|
+
raise ValueError("mean of empty iterable")
|
|
164
|
+
acc / count
|
|
165
|
+
|
|
166
|
+
max = self =>
|
|
167
|
+
let it = self.iter
|
|
168
|
+
let v
|
|
169
|
+
try:
|
|
170
|
+
v = next(it)
|
|
171
|
+
except StopIteration(value=value):
|
|
172
|
+
raise ValueError("max of empty iterable")
|
|
173
|
+
|
|
174
|
+
for i in it:
|
|
175
|
+
if i > v:
|
|
176
|
+
v = i
|
|
177
|
+
v
|
|
178
|
+
|
|
179
|
+
min = self =>
|
|
180
|
+
let it = self.iter
|
|
181
|
+
let v
|
|
182
|
+
try:
|
|
183
|
+
v = next(it)
|
|
184
|
+
except StopIteration(value=value):
|
|
185
|
+
raise ValueError("min of empty iterable")
|
|
186
|
+
|
|
187
|
+
for i in it:
|
|
188
|
+
if i < v:
|
|
189
|
+
v = i
|
|
190
|
+
v
|
|
140
191
|
|
|
141
192
|
list = self =>
|
|
142
193
|
list(self.iter)
|
|
@@ -164,9 +215,11 @@ export Iterable = ExtensionTrait& class(Traversable, Trait):
|
|
|
164
215
|
|
|
165
216
|
# TODO: can special case traversals be generalized?
|
|
166
217
|
|
|
218
|
+
if not hasattr(m, "apply"):
|
|
219
|
+
raise ValueError(f"Can only traverse with an Applicative type.")
|
|
220
|
+
|
|
167
221
|
# The Result traversal of a list is short-circuiting.
|
|
168
|
-
|
|
169
|
-
if m matches Result() or not hasattr(m, "apply"):
|
|
222
|
+
if m matches Result():
|
|
170
223
|
if not m.ok:
|
|
171
224
|
return m
|
|
172
225
|
|
|
@@ -185,6 +238,16 @@ export Iterable = ExtensionTrait& class(Traversable, Trait):
|
|
|
185
238
|
acc.append(f(v))
|
|
186
239
|
return Async.gather(*acc)
|
|
187
240
|
|
|
241
|
+
if m matches AsyncMemo():
|
|
242
|
+
return AsyncMemo(ctx =>
|
|
243
|
+
let acc = [m.f(ctx)]
|
|
244
|
+
|
|
245
|
+
for v in it:
|
|
246
|
+
acc.append(f(v).f(ctx))
|
|
247
|
+
|
|
248
|
+
return Async.gather(*acc)
|
|
249
|
+
)
|
|
250
|
+
|
|
188
251
|
# Fall back to default applicative semantics.
|
|
189
252
|
let acc = m.map([$])
|
|
190
253
|
for v in it:
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import collections
|
|
3
|
+
import .functional.Result
|
|
4
|
+
|
|
5
|
+
export Regex = class:
|
|
6
|
+
__slots__ = ("pattern",)
|
|
7
|
+
|
|
8
|
+
__init__ = (self, pattern) =>
|
|
9
|
+
pattern match:
|
|
10
|
+
re.Pattern() => self.pattern = pattern
|
|
11
|
+
str() => self.pattern = re.compile(pattern)
|
|
12
|
+
Regex(pattern=pattern) => self.pattern = pattern
|
|
13
|
+
default => raise TypeError(
|
|
14
|
+
f"Expected a str or pattern, but got {type(pattern).__name__}"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__repr__ = self => f"Regex({self.pattern})"
|
|
18
|
+
|
|
19
|
+
match = (self, string) =>
|
|
20
|
+
if not isinstance(string, str):
|
|
21
|
+
raise TypeError(f"Expected a string, but got {type(string).__name__}")
|
|
22
|
+
|
|
23
|
+
self.pattern.match(string).result.map(Regex.Match)
|
|
24
|
+
|
|
25
|
+
search = (self, string) =>
|
|
26
|
+
if not isinstance(string, str):
|
|
27
|
+
raise TypeError(f"Expected a string, but got {type(string).__name__}")
|
|
28
|
+
|
|
29
|
+
self.pattern.search(string).result.map(Regex.Match)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
Match = collections.abc.Sequence.register& class:
|
|
33
|
+
__slots__ = ("_match",)
|
|
34
|
+
__match_args__ = ("match",)
|
|
35
|
+
|
|
36
|
+
__init__ = (self, match) =>
|
|
37
|
+
if not isinstance(match, re.Match):
|
|
38
|
+
raise TypeError(f"Expected a re.Match, but got {type(match).__name__}")
|
|
39
|
+
|
|
40
|
+
self._match = match
|
|
41
|
+
|
|
42
|
+
__iter__ = self => iter(self._match.groups())
|
|
43
|
+
__len__ = self => len(self._match.groups())
|
|
44
|
+
__getitem__ = (self, index) => self._match[index]
|
|
45
|
+
|
|
46
|
+
match = property& self => self._match.group(0)
|
|
47
|
+
|
|
48
|
+
__repr__ = self => f"Match({self._match})"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
Extension.method(str, "match")& (regex, str) => Regex(regex).match(str)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Note: the below methods have arguments in reverse order to Python's re module.
|
|
55
|
+
|
|
56
|
+
Extension.method(str, "matches")& (str, regex) =>
|
|
57
|
+
Regex(regex).match(str)
|
|
58
|
+
|
|
59
|
+
Extension.method(str, "search")& (str, regex) =>
|
|
60
|
+
Regex(regex).search(str)
|
|
@@ -43,6 +43,7 @@ __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
|
+
async_memo_value=dummy("memo"),
|
|
46
47
|
op_map=dummy("?"),
|
|
47
48
|
op_coal=("??"),
|
|
48
49
|
Ok=dummy("try-expr"),
|