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.
Files changed (136) hide show
  1. {koatl-0.1.29 → koatl-0.1.31}/Cargo.lock +1 -1
  2. {koatl-0.1.29 → koatl-0.1.31}/PKG-INFO +1 -1
  3. {koatl-0.1.29 → koatl-0.1.31}/koatl/Cargo.toml +1 -1
  4. koatl-0.1.31/koatl/python/koatl/prelude/__init__.tl +4 -0
  5. koatl-0.1.31/koatl/python/koatl/prelude/functional/__init__.tl +26 -0
  6. koatl-0.1.31/koatl/python/koatl/prelude/functional/algebra.tl +72 -0
  7. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/prelude/functional/async.tl +9 -3
  8. koatl-0.1.31/koatl/python/koatl/prelude/functional/env.tl +42 -0
  9. {koatl-0.1.29 → koatl-0.1.31/koatl}/python/koatl/prelude/functional/list.tl +6 -2
  10. koatl-0.1.31/koatl/python/koatl/prelude/functional/memo.tl +121 -0
  11. {koatl-0.1.29 → koatl-0.1.31/koatl}/python/koatl/prelude/functional/result.tl +33 -13
  12. koatl-0.1.31/koatl/python/koatl/prelude/io.tl +5 -0
  13. koatl-0.1.31/koatl/python/koatl/prelude/iterable.tl +194 -0
  14. {koatl-0.1.29 → koatl-0.1.31/koatl}/python/koatl/runtime/__init__.py +1 -1
  15. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/runtime/helpers.py +11 -7
  16. {koatl-0.1.29 → koatl-0.1.31/koatl}/python/koatl/runtime/record.py +7 -6
  17. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/runtime/virtual.py +3 -3
  18. {koatl-0.1.29 → koatl-0.1.31}/koatl/src/emit_py.rs +17 -13
  19. koatl-0.1.31/koatl/tests/e2e/prelude/env.tl +9 -0
  20. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/iterables.tl +10 -0
  21. koatl-0.1.31/koatl/tests/e2e/prelude/list.tl +47 -0
  22. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/memo.tl +30 -1
  23. koatl-0.1.31/koatl/tests/e2e/prelude/result.tl +99 -0
  24. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/resolve_scopes.rs +4 -0
  25. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/transform.rs +3 -6
  26. koatl-0.1.31/python/koatl/prelude/__init__.tl +4 -0
  27. koatl-0.1.31/python/koatl/prelude/functional/__init__.tl +26 -0
  28. koatl-0.1.31/python/koatl/prelude/functional/algebra.tl +72 -0
  29. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/prelude/functional/async.tl +9 -3
  30. koatl-0.1.31/python/koatl/prelude/functional/env.tl +42 -0
  31. {koatl-0.1.29/koatl → koatl-0.1.31}/python/koatl/prelude/functional/list.tl +6 -2
  32. koatl-0.1.31/python/koatl/prelude/functional/memo.tl +121 -0
  33. {koatl-0.1.29/koatl → koatl-0.1.31}/python/koatl/prelude/functional/result.tl +33 -13
  34. koatl-0.1.31/python/koatl/prelude/io.tl +5 -0
  35. koatl-0.1.31/python/koatl/prelude/iterable.tl +194 -0
  36. {koatl-0.1.29/koatl → koatl-0.1.31}/python/koatl/runtime/__init__.py +1 -1
  37. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/runtime/helpers.py +11 -7
  38. {koatl-0.1.29/koatl → koatl-0.1.31}/python/koatl/runtime/record.py +7 -6
  39. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/runtime/virtual.py +3 -3
  40. koatl-0.1.29/koatl/python/koatl/prelude/__init__.tl +0 -2
  41. koatl-0.1.29/koatl/python/koatl/prelude/functional/__init__.tl +0 -37
  42. koatl-0.1.29/koatl/python/koatl/prelude/functional/algebra.tl +0 -42
  43. koatl-0.1.29/koatl/python/koatl/prelude/functional/memo.tl +0 -83
  44. koatl-0.1.29/koatl/python/koatl/prelude/functional/reader.tl +0 -31
  45. koatl-0.1.29/koatl/python/koatl/prelude/iterable.tl +0 -79
  46. koatl-0.1.29/koatl/tests/e2e/prelude/list.tl +0 -18
  47. koatl-0.1.29/koatl/tests/e2e/prelude/reader.tl +0 -11
  48. koatl-0.1.29/koatl/tests/e2e/prelude/result.tl +0 -29
  49. koatl-0.1.29/python/koatl/prelude/__init__.tl +0 -2
  50. koatl-0.1.29/python/koatl/prelude/functional/__init__.tl +0 -37
  51. koatl-0.1.29/python/koatl/prelude/functional/algebra.tl +0 -42
  52. koatl-0.1.29/python/koatl/prelude/functional/memo.tl +0 -83
  53. koatl-0.1.29/python/koatl/prelude/functional/reader.tl +0 -31
  54. koatl-0.1.29/python/koatl/prelude/iterable.tl +0 -79
  55. {koatl-0.1.29 → koatl-0.1.31}/Cargo.toml +0 -0
  56. {koatl-0.1.29 → koatl-0.1.31}/README.md +0 -0
  57. {koatl-0.1.29 → koatl-0.1.31}/koatl/.github/workflows/CI.yml +0 -0
  58. {koatl-0.1.29 → koatl-0.1.31}/koatl/.gitignore +0 -0
  59. {koatl-0.1.29 → koatl-0.1.31}/koatl/LICENSE +0 -0
  60. {koatl-0.1.29 → koatl-0.1.31}/koatl/README.md +0 -0
  61. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/__init__.py +0 -0
  62. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/__main__.py +0 -0
  63. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/cli.py +0 -0
  64. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/notebook/__init__.py +0 -0
  65. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/notebook/magic.py +0 -0
  66. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  67. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/runtime/classes.py +0 -0
  68. {koatl-0.1.29 → koatl-0.1.31}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  69. {koatl-0.1.29 → koatl-0.1.31}/koatl/requirements.txt +0 -0
  70. {koatl-0.1.29 → koatl-0.1.31}/koatl/src/lib.rs +0 -0
  71. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/coal.tl +0 -0
  72. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/containers.tl +0 -0
  73. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/decorators.tl +0 -0
  74. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  75. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/destructure.tl +0 -0
  76. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  77. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/fstr.tl +0 -0
  78. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/functions.tl +0 -0
  79. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/generator.tl +0 -0
  80. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/if_expr.tl +0 -0
  81. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/imports.tl +0 -0
  82. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/loops.tl +0 -0
  83. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/match.tl +0 -0
  84. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/nary-list.tl +0 -0
  85. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/placeholder.tl +0 -0
  86. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/precedence.tl +0 -0
  87. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/record.tl +0 -0
  88. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/scopes.tl +0 -0
  89. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  90. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  91. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/destructure.tl +0 -0
  92. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/async.tl +0 -0
  93. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
  94. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/slice.tl +0 -0
  95. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/try.tl +0 -0
  96. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  97. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/util/__init__.py +0 -0
  98. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/util/module0.tl +0 -0
  99. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/util/module1.tl +0 -0
  100. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/e2e/util/module2.tl +0 -0
  101. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/arith.tl +0 -0
  102. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/assign.tl +0 -0
  103. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/block-comments.tl +0 -0
  104. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/deco.tl +0 -0
  105. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/func.tl +0 -0
  106. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/parse/matches.tl +0 -0
  107. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/test_e2e.py +0 -0
  108. {koatl-0.1.29 → koatl-0.1.31}/koatl/tests/test_parse.py +0 -0
  109. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/Cargo.toml +0 -0
  110. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/Cargo.toml +0 -0
  111. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/ast.rs +0 -0
  112. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/lexer.rs +0 -0
  113. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/lib.rs +0 -0
  114. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/parser.rs +0 -0
  115. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/src/util.rs +0 -0
  116. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/parser/tests/lexer.rs +0 -0
  117. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/inference.rs +0 -0
  118. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/lib.rs +0 -0
  119. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/main.rs +0 -0
  120. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/parse_timer.rs +0 -0
  121. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/parser.rs +0 -0
  122. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/py/ast.rs +0 -0
  123. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/py/emit.rs +0 -0
  124. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/py/mod.rs +0 -0
  125. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/py/util.rs +0 -0
  126. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/types.rs +0 -0
  127. {koatl-0.1.29 → koatl-0.1.31}/koatl-core/src/util.rs +0 -0
  128. {koatl-0.1.29 → koatl-0.1.31}/pyproject.toml +0 -0
  129. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/__init__.py +0 -0
  130. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/__main__.py +0 -0
  131. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/cli.py +0 -0
  132. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/notebook/__init__.py +0 -0
  133. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/notebook/magic.py +0 -0
  134. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/prelude/functional/async_util.py +0 -0
  135. {koatl-0.1.29 → koatl-0.1.31}/python/koatl/runtime/classes.py +0 -0
  136. {koatl-0.1.29 → koatl-0.1.31}/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.29"
102
+ version = "0.1.31"
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.29
3
+ Version: 0.1.31
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.29"
3
+ version = "0.1.31"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -0,0 +1,4 @@
1
+ export import .functional.*
2
+
3
+ export import .iterable.Iterable
4
+ export import .io
@@ -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
- return result
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
- # TODO: Why is asyncio.gather eager?
43
- gather = staticmethod& (*args) => Async.from_generator_fn& () => yield from asyncio.gather(*args)
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
- traverse = Iterable.traverse
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
- let v = Result(f(self.value))
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.pure(value)
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
- # if the object is already created.
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,5 @@
1
+ export read = (filename) =>
2
+ let desc = open(filename, "r")
3
+ let data = desc.read()
4
+ desc.close()
5
+ data
@@ -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__},