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.
Files changed (131) hide show
  1. {koatl-0.1.31 → koatl-0.1.33}/Cargo.lock +1 -1
  2. {koatl-0.1.31 → koatl-0.1.33}/PKG-INFO +1 -1
  3. {koatl-0.1.31 → koatl-0.1.33}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.31 → koatl-0.1.33/koatl}/python/koatl/prelude/__init__.tl +3 -1
  5. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/async.tl +6 -5
  6. {koatl-0.1.31 → koatl-0.1.33/koatl}/python/koatl/prelude/functional/env.tl +6 -3
  7. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/list.tl +2 -6
  8. koatl-0.1.33/koatl/python/koatl/prelude/functional/memo.tl +187 -0
  9. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/result.tl +52 -35
  10. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/iterable.tl +70 -7
  11. koatl-0.1.33/koatl/python/koatl/prelude/regex.tl +60 -0
  12. {koatl-0.1.31 → koatl-0.1.33/koatl}/python/koatl/runtime/__init__.py +1 -0
  13. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/runtime/classes.py +23 -3
  14. {koatl-0.1.31 → koatl-0.1.33/koatl}/python/koatl/runtime/helpers.py +11 -27
  15. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/runtime/record.py +19 -2
  16. koatl-0.1.33/koatl/python/koatl/runtime/virtual.py +103 -0
  17. {koatl-0.1.31 → koatl-0.1.33}/koatl/src/emit_py.rs +18 -2
  18. koatl-0.1.33/koatl/tests/e2e/base/data.txt +1 -0
  19. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/loops.tl +1 -1
  20. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/match.tl +1 -1
  21. koatl-0.1.33/koatl/tests/e2e/base/with.tl +6 -0
  22. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/list.tl +2 -2
  23. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/memo.tl +19 -6
  24. koatl-0.1.33/koatl/tests/e2e/prelude/result.tl +55 -0
  25. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/try.tl +1 -1
  26. koatl-0.1.33/koatl/tests/e2e/prelude/virtual.tl +32 -0
  27. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/test_e2e.py +2 -0
  28. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/ast.rs +3 -2
  29. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/lexer.rs +58 -10
  30. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/parser.rs +89 -66
  31. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/util.rs +0 -8
  32. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/inference.rs +5 -5
  33. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/py/ast.rs +2 -1
  34. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/py/emit.rs +21 -4
  35. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/py/util.rs +9 -1
  36. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/resolve_scopes.rs +25 -10
  37. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/transform.rs +37 -23
  38. {koatl-0.1.31/koatl → koatl-0.1.33}/python/koatl/prelude/__init__.tl +3 -1
  39. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/async.tl +6 -5
  40. {koatl-0.1.31/koatl → koatl-0.1.33}/python/koatl/prelude/functional/env.tl +6 -3
  41. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/list.tl +2 -6
  42. koatl-0.1.33/python/koatl/prelude/functional/memo.tl +187 -0
  43. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/result.tl +52 -35
  44. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/iterable.tl +70 -7
  45. koatl-0.1.33/python/koatl/prelude/regex.tl +60 -0
  46. {koatl-0.1.31/koatl → koatl-0.1.33}/python/koatl/runtime/__init__.py +1 -0
  47. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/runtime/classes.py +23 -3
  48. {koatl-0.1.31/koatl → koatl-0.1.33}/python/koatl/runtime/helpers.py +11 -27
  49. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/runtime/record.py +19 -2
  50. koatl-0.1.33/python/koatl/runtime/virtual.py +103 -0
  51. koatl-0.1.31/koatl/python/koatl/prelude/functional/async_util.py +0 -15
  52. koatl-0.1.31/koatl/python/koatl/prelude/functional/memo.tl +0 -121
  53. koatl-0.1.31/koatl/python/koatl/runtime/virtual.py +0 -103
  54. koatl-0.1.31/koatl/tests/e2e/destructure.tl +0 -0
  55. koatl-0.1.31/koatl/tests/e2e/prelude/result.tl +0 -99
  56. koatl-0.1.31/koatl/tests/e2e/prelude/virtual.tl +0 -14
  57. koatl-0.1.31/koatl-core/parser/tests/lexer.rs +0 -224
  58. koatl-0.1.31/python/koatl/prelude/functional/async_util.py +0 -15
  59. koatl-0.1.31/python/koatl/prelude/functional/memo.tl +0 -121
  60. koatl-0.1.31/python/koatl/runtime/virtual.py +0 -103
  61. {koatl-0.1.31 → koatl-0.1.33}/Cargo.toml +0 -0
  62. {koatl-0.1.31 → koatl-0.1.33}/README.md +0 -0
  63. {koatl-0.1.31 → koatl-0.1.33}/koatl/.github/workflows/CI.yml +0 -0
  64. {koatl-0.1.31 → koatl-0.1.33}/koatl/.gitignore +0 -0
  65. {koatl-0.1.31 → koatl-0.1.33}/koatl/LICENSE +0 -0
  66. {koatl-0.1.31 → koatl-0.1.33}/koatl/README.md +0 -0
  67. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/__init__.py +0 -0
  68. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/__main__.py +0 -0
  69. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/cli.py +0 -0
  70. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/notebook/__init__.py +0 -0
  71. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/notebook/magic.py +0 -0
  72. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
  73. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/functional/algebra.tl +0 -0
  74. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/prelude/io.tl +0 -0
  75. {koatl-0.1.31 → koatl-0.1.33}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  76. {koatl-0.1.31 → koatl-0.1.33}/koatl/requirements.txt +0 -0
  77. {koatl-0.1.31 → koatl-0.1.33}/koatl/src/lib.rs +0 -0
  78. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/coal.tl +0 -0
  79. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/containers.tl +0 -0
  80. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/decorators.tl +0 -0
  81. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  82. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/destructure.tl +0 -0
  83. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  84. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/fstr.tl +0 -0
  85. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/functions.tl +0 -0
  86. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/generator.tl +0 -0
  87. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/if_expr.tl +0 -0
  88. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/imports.tl +0 -0
  89. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/nary-list.tl +0 -0
  90. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/placeholder.tl +0 -0
  91. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/precedence.tl +0 -0
  92. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/record.tl +0 -0
  93. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/scopes.tl +0 -0
  94. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  95. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  96. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/async.tl +0 -0
  97. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
  98. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/env.tl +0 -0
  99. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  100. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/prelude/slice.tl +0 -0
  101. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/util/__init__.py +0 -0
  102. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/util/module0.tl +0 -0
  103. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/util/module1.tl +0 -0
  104. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/e2e/util/module2.tl +0 -0
  105. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/arith.tl +0 -0
  106. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/assign.tl +0 -0
  107. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/block-comments.tl +0 -0
  108. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/deco.tl +0 -0
  109. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/func.tl +0 -0
  110. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/parse/matches.tl +0 -0
  111. {koatl-0.1.31 → koatl-0.1.33}/koatl/tests/test_parse.py +0 -0
  112. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/Cargo.toml +0 -0
  113. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/Cargo.toml +0 -0
  114. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/parser/src/lib.rs +0 -0
  115. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/lib.rs +0 -0
  116. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/main.rs +0 -0
  117. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/parse_timer.rs +0 -0
  118. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/parser.rs +0 -0
  119. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/py/mod.rs +0 -0
  120. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/types.rs +0 -0
  121. {koatl-0.1.31 → koatl-0.1.33}/koatl-core/src/util.rs +0 -0
  122. {koatl-0.1.31 → koatl-0.1.33}/pyproject.toml +0 -0
  123. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/__init__.py +0 -0
  124. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/__main__.py +0 -0
  125. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/cli.py +0 -0
  126. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/notebook/__init__.py +0 -0
  127. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/notebook/magic.py +0 -0
  128. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/__init__.tl +0 -0
  129. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/functional/algebra.tl +0 -0
  130. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/prelude/io.tl +0 -0
  131. {koatl-0.1.31 → koatl-0.1.33}/python/koatl/runtime/meta_finder.py +0 -0
@@ -99,7 +99,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
99
99
 
100
100
  [[package]]
101
101
  name = "koatl"
102
- version = "0.1.31"
102
+ version = "0.1.33"
103
103
  dependencies = [
104
104
  "ariadne",
105
105
  "koatl-core",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koatl
3
- Version: 0.1.31
3
+ Version: 0.1.33
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "koatl"
3
- version = "0.1.31"
3
+ version = "0.1.33"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -1,4 +1,6 @@
1
1
  export import .functional.*
2
2
 
3
3
  export import .iterable.Iterable
4
- export import .io
4
+ export import .io
5
+
6
+ export import .regex.*
@@ -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(async_util.to_coro(self))
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 yield from result.__await__()
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.f(ctx)
31
- v
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 = TraitProperty& self => len(self)
22
+ len = property& self => len(self)
23
23
 
24
24
 
25
- for name, method in List.__dict__:
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.ok then Ok(value) else Err(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 = Result(gen.send(self.value))
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 = class(MonadOnce):
76
- __slots__ = ("value",)
77
- __init__ = (self, value) =>
78
- if not hasattr(value, "bind_once"):
79
- raise TypeError("Expected a MonadOnce value")
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
- self.value = value
91
+ self.value = value
82
92
 
83
- __repr__ = self => f"Result.T({repr(self.value)})"
93
+ __repr__ = self => f"Result.T({typ.__name__})({repr(self.value)})"
84
94
 
85
- pure = (self, x) => self.value.pure(Result.pure(x))
95
+ pure = (self, x) => cls(typ.pure(Result.pure(x)))
86
96
 
87
- bind_once = (self, f) =>
88
- self.value.bind_once(x =>
89
- x = Result(x)
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
- if not x.ok:
92
- return self.value.pure(x)
102
+ if not x.ok:
103
+ return typ.pure(x)
93
104
 
94
- f(x.value)
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.value)}")
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.ok:
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.ok:
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 = ExtensionTrait& class(Traversable, Trait):
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
- for_each = (self, f) =>
156
+ mean = self =>
157
+ let acc = 0
158
+ let count = 0
138
159
  for i in self:
139
- f(i)
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
- # Fall back to Result semantics for bare types.
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"),