koatl 0.1.31__tar.gz → 0.1.34__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.34}/Cargo.lock +1 -1
  2. {koatl-0.1.31 → koatl-0.1.34}/PKG-INFO +1 -1
  3. {koatl-0.1.31 → koatl-0.1.34}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.31 → koatl-0.1.34/koatl}/python/koatl/prelude/__init__.tl +3 -1
  5. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/async.tl +6 -5
  6. {koatl-0.1.31 → koatl-0.1.34/koatl}/python/koatl/prelude/functional/env.tl +6 -3
  7. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/list.tl +2 -6
  8. koatl-0.1.34/koatl/python/koatl/prelude/functional/memo.tl +187 -0
  9. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/result.tl +52 -35
  10. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/prelude/iterable.tl +122 -9
  11. koatl-0.1.34/koatl/python/koatl/prelude/regex.tl +60 -0
  12. {koatl-0.1.31 → koatl-0.1.34/koatl}/python/koatl/runtime/__init__.py +1 -0
  13. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/runtime/classes.py +23 -3
  14. {koatl-0.1.31 → koatl-0.1.34/koatl}/python/koatl/runtime/helpers.py +11 -27
  15. {koatl-0.1.31 → koatl-0.1.34/koatl}/python/koatl/runtime/record.py +29 -2
  16. koatl-0.1.34/koatl/python/koatl/runtime/virtual.py +103 -0
  17. {koatl-0.1.31 → koatl-0.1.34}/koatl/src/emit_py.rs +21 -2
  18. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/coal.tl +1 -1
  19. koatl-0.1.34/koatl/tests/e2e/base/data.txt +1 -0
  20. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/loops.tl +1 -1
  21. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/match.tl +1 -1
  22. koatl-0.1.34/koatl/tests/e2e/base/with.tl +6 -0
  23. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/prelude/list.tl +2 -2
  24. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/prelude/memo.tl +19 -6
  25. koatl-0.1.34/koatl/tests/e2e/prelude/result.tl +55 -0
  26. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/prelude/try.tl +3 -3
  27. koatl-0.1.34/koatl/tests/e2e/prelude/virtual.tl +32 -0
  28. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/test_e2e.py +2 -0
  29. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/parser/src/ast.rs +12 -7
  30. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/parser/src/lexer.rs +98 -28
  31. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/parser/src/parser.rs +126 -89
  32. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/parser/src/util.rs +0 -8
  33. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/inference.rs +5 -5
  34. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/py/ast.rs +6 -2
  35. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/py/emit.rs +64 -22
  36. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/py/util.rs +10 -2
  37. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/resolve_scopes.rs +26 -11
  38. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/transform.rs +47 -29
  39. {koatl-0.1.31/koatl → koatl-0.1.34}/python/koatl/prelude/__init__.tl +3 -1
  40. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/prelude/functional/async.tl +6 -5
  41. {koatl-0.1.31/koatl → koatl-0.1.34}/python/koatl/prelude/functional/env.tl +6 -3
  42. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/prelude/functional/list.tl +2 -6
  43. koatl-0.1.34/python/koatl/prelude/functional/memo.tl +187 -0
  44. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/prelude/functional/result.tl +52 -35
  45. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/prelude/iterable.tl +122 -9
  46. koatl-0.1.34/python/koatl/prelude/regex.tl +60 -0
  47. {koatl-0.1.31/koatl → koatl-0.1.34}/python/koatl/runtime/__init__.py +1 -0
  48. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/runtime/classes.py +23 -3
  49. {koatl-0.1.31/koatl → koatl-0.1.34}/python/koatl/runtime/helpers.py +11 -27
  50. {koatl-0.1.31/koatl → koatl-0.1.34}/python/koatl/runtime/record.py +29 -2
  51. koatl-0.1.34/python/koatl/runtime/virtual.py +103 -0
  52. koatl-0.1.31/koatl/python/koatl/prelude/functional/async_util.py +0 -15
  53. koatl-0.1.31/koatl/python/koatl/prelude/functional/memo.tl +0 -121
  54. koatl-0.1.31/koatl/python/koatl/runtime/virtual.py +0 -103
  55. koatl-0.1.31/koatl/tests/e2e/destructure.tl +0 -0
  56. koatl-0.1.31/koatl/tests/e2e/prelude/result.tl +0 -99
  57. koatl-0.1.31/koatl/tests/e2e/prelude/virtual.tl +0 -14
  58. koatl-0.1.31/koatl-core/parser/tests/lexer.rs +0 -224
  59. koatl-0.1.31/python/koatl/prelude/functional/async_util.py +0 -15
  60. koatl-0.1.31/python/koatl/prelude/functional/memo.tl +0 -121
  61. koatl-0.1.31/python/koatl/runtime/virtual.py +0 -103
  62. {koatl-0.1.31 → koatl-0.1.34}/Cargo.toml +0 -0
  63. {koatl-0.1.31 → koatl-0.1.34}/README.md +0 -0
  64. {koatl-0.1.31 → koatl-0.1.34}/koatl/.github/workflows/CI.yml +0 -0
  65. {koatl-0.1.31 → koatl-0.1.34}/koatl/.gitignore +0 -0
  66. {koatl-0.1.31 → koatl-0.1.34}/koatl/LICENSE +0 -0
  67. {koatl-0.1.31 → koatl-0.1.34}/koatl/README.md +0 -0
  68. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/__init__.py +0 -0
  69. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/__main__.py +0 -0
  70. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/cli.py +0 -0
  71. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/notebook/__init__.py +0 -0
  72. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/notebook/magic.py +0 -0
  73. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
  74. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/prelude/functional/algebra.tl +0 -0
  75. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/prelude/io.tl +0 -0
  76. {koatl-0.1.31 → koatl-0.1.34}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  77. {koatl-0.1.31 → koatl-0.1.34}/koatl/requirements.txt +0 -0
  78. {koatl-0.1.31 → koatl-0.1.34}/koatl/src/lib.rs +0 -0
  79. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/containers.tl +0 -0
  80. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/decorators.tl +0 -0
  81. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  82. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/destructure.tl +0 -0
  83. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  84. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/fstr.tl +0 -0
  85. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/functions.tl +0 -0
  86. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/generator.tl +0 -0
  87. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/if_expr.tl +0 -0
  88. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/imports.tl +0 -0
  89. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/nary-list.tl +0 -0
  90. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/placeholder.tl +0 -0
  91. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/precedence.tl +0 -0
  92. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/record.tl +0 -0
  93. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/scopes.tl +0 -0
  94. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  95. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  96. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/prelude/async.tl +0 -0
  97. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
  98. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/prelude/env.tl +0 -0
  99. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  100. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/prelude/slice.tl +0 -0
  101. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/util/__init__.py +0 -0
  102. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/util/module0.tl +0 -0
  103. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/util/module1.tl +0 -0
  104. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/e2e/util/module2.tl +0 -0
  105. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/parse/arith.tl +0 -0
  106. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/parse/assign.tl +0 -0
  107. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/parse/block-comments.tl +0 -0
  108. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/parse/deco.tl +0 -0
  109. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/parse/func.tl +0 -0
  110. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/parse/matches.tl +0 -0
  111. {koatl-0.1.31 → koatl-0.1.34}/koatl/tests/test_parse.py +0 -0
  112. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/Cargo.toml +0 -0
  113. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/parser/Cargo.toml +0 -0
  114. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/parser/src/lib.rs +0 -0
  115. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/lib.rs +0 -0
  116. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/main.rs +0 -0
  117. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/parse_timer.rs +0 -0
  118. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/parser.rs +0 -0
  119. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/py/mod.rs +0 -0
  120. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/types.rs +0 -0
  121. {koatl-0.1.31 → koatl-0.1.34}/koatl-core/src/util.rs +0 -0
  122. {koatl-0.1.31 → koatl-0.1.34}/pyproject.toml +0 -0
  123. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/__init__.py +0 -0
  124. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/__main__.py +0 -0
  125. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/cli.py +0 -0
  126. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/notebook/__init__.py +0 -0
  127. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/notebook/magic.py +0 -0
  128. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/prelude/functional/__init__.tl +0 -0
  129. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/prelude/functional/algebra.tl +0 -0
  130. {koatl-0.1.31 → koatl-0.1.34}/python/koatl/prelude/io.tl +0 -0
  131. {koatl-0.1.31 → koatl-0.1.34}/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.34"
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.34
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.34"
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")& self => 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, Result)
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) =>
@@ -51,6 +50,22 @@ export Iterable = ExtensionTrait& class(Traversable, Trait):
51
50
 
52
51
  filter = (self, f) => builtins.filter(f, self.iter)
53
52
 
53
+ filter_map = (self, f=None) =>
54
+ let impl = () =>
55
+ if f === None:
56
+ for i in self.iter:
57
+ if Result(i) matches Ok(value):
58
+ yield value
59
+ else:
60
+ for i in self.iter:
61
+ if Result(f(i)) matches Ok(value):
62
+ yield value
63
+
64
+ impl.__name__ = f"filter_map"
65
+ impl.__qualname__ = f"Iterable.filter_map"
66
+
67
+ impl()
68
+
54
69
  flat_map = (self, f) => itertools.chain.from_iterable(self.map(f))
55
70
 
56
71
  reverse = self => reversed(list(self.iter))
@@ -60,8 +75,16 @@ export Iterable = ExtensionTrait& class(Traversable, Trait):
60
75
 
61
76
  copy = self => itertools.tee(self.iter)
62
77
 
63
-
64
- count = self => len(self.iter)
78
+ count = (self, f=None) =>
79
+ let acc = 0
80
+ if f === None:
81
+ for _ in self:
82
+ acc += 1
83
+ else:
84
+ for i in self:
85
+ if f(i):
86
+ acc += 1
87
+ acc
65
88
 
66
89
  fold = (self, init, f) =>
67
90
  let acc = init
@@ -128,15 +151,93 @@ export Iterable = ExtensionTrait& class(Traversable, Trait):
128
151
  return Ok(i)
129
152
  return Err()
130
153
 
154
+ for_each = (self, f) =>
155
+ for i in self:
156
+ f(i)
157
+
158
+ unique = self =>
159
+ let seen = set()
160
+ let impl = () =>
161
+ for i in self:
162
+ if not seen.__contains__(i):
163
+ seen.add(i)
164
+ yield i
165
+
166
+ impl.__name__ = f"unique"
167
+ impl.__qualname__ = f"Iterable.unique"
168
+
169
+ impl()
170
+
171
+ join_str = (self, sep="") =>
172
+ sep.join(self.map(str))
173
+
131
174
  sum = self =>
132
175
  let acc = 0
133
176
  for i in self:
134
177
  acc = acc + i
135
178
  acc
136
179
 
137
- for_each = (self, f) =>
180
+ mean = self =>
181
+ let acc = 0
182
+ let count = 0
138
183
  for i in self:
139
- f(i)
184
+ acc = acc + i
185
+ count = count + 1
186
+ if count == 0:
187
+ raise ValueError("mean of empty iterable")
188
+ acc / count
189
+
190
+ max = (self, key=None) =>
191
+ let it = self.iter
192
+ let v, m
193
+ try:
194
+ v = next(it)
195
+ if key === None:
196
+ m = v
197
+ else:
198
+ m = key(v)
199
+ except StopIteration(value=value):
200
+ raise ValueError("max of empty iterable")
201
+
202
+
203
+ if key === None:
204
+ for i in it:
205
+ if i > m:
206
+ v = i
207
+ m = i
208
+ else:
209
+ for i in it:
210
+ let ki = key(i)
211
+ if ki > m:
212
+ v = i
213
+ m = ki
214
+
215
+ v
216
+
217
+ min = (self, key=None) =>
218
+ let it = self.iter
219
+ let v, m
220
+ try:
221
+ v = next(it)
222
+ if key === None:
223
+ m = v
224
+ else:
225
+ m = key(v)
226
+ except StopIteration(value=value):
227
+ raise ValueError("min of empty iterable")
228
+
229
+ if key === None:
230
+ for i in it:
231
+ if i < m:
232
+ v = i
233
+ m = i
234
+ else:
235
+ for i in it:
236
+ let ki = key(i)
237
+ if ki < m:
238
+ v = i
239
+ m = ki
240
+ v
140
241
 
141
242
  list = self =>
142
243
  list(self.iter)
@@ -164,9 +265,11 @@ export Iterable = ExtensionTrait& class(Traversable, Trait):
164
265
 
165
266
  # TODO: can special case traversals be generalized?
166
267
 
268
+ if not hasattr(m, "apply"):
269
+ raise ValueError(f"Can only traverse with an Applicative type.")
270
+
167
271
  # 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"):
272
+ if m matches Result():
170
273
  if not m.ok:
171
274
  return m
172
275
 
@@ -185,6 +288,16 @@ export Iterable = ExtensionTrait& class(Traversable, Trait):
185
288
  acc.append(f(v))
186
289
  return Async.gather(*acc)
187
290
 
291
+ if m matches AsyncMemo():
292
+ return AsyncMemo(ctx =>
293
+ let acc = [m.f(ctx)]
294
+
295
+ for v in it:
296
+ acc.append(f(v).f(ctx))
297
+
298
+ return Async.gather(*acc)
299
+ )
300
+
188
301
  # Fall back to default applicative semantics.
189
302
  let acc = m.map([$])
190
303
  for v in it: