koatl 0.1.24__tar.gz → 0.1.26__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 (118) hide show
  1. {koatl-0.1.24 → koatl-0.1.26}/Cargo.lock +1 -1
  2. {koatl-0.1.24 → koatl-0.1.26}/PKG-INFO +1 -1
  3. {koatl-0.1.24 → koatl-0.1.26}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/prelude/functional/__init__.tl +1 -1
  5. koatl-0.1.26/koatl/python/koatl/prelude/functional/algebra.tl +46 -0
  6. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/prelude/functional/async.tl +3 -3
  7. {koatl-0.1.24 → koatl-0.1.26/koatl}/python/koatl/prelude/functional/list.tl +1 -1
  8. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/prelude/functional/memo.tl +21 -11
  9. {koatl-0.1.24 → koatl-0.1.26/koatl}/python/koatl/prelude/functional/reader.tl +2 -2
  10. {koatl-0.1.24 → koatl-0.1.26/koatl}/python/koatl/prelude/functional/result.tl +45 -29
  11. {koatl-0.1.24 → koatl-0.1.26/koatl}/python/koatl/prelude/iterable.tl +2 -2
  12. {koatl-0.1.24 → koatl-0.1.26/koatl}/python/koatl/runtime/__init__.py +16 -7
  13. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/runtime/helpers.py +9 -6
  14. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/runtime/virtual.py +2 -1
  15. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/coal.tl +0 -12
  16. koatl-0.1.26/koatl/tests/e2e/base/loops.tl +25 -0
  17. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/match.tl +0 -2
  18. koatl-0.1.26/koatl/tests/e2e/base/short_circuit.tl +42 -0
  19. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/prelude/memo.tl +1 -1
  20. {koatl-0.1.24/koatl/tests/e2e/base → koatl-0.1.26/koatl/tests/e2e/prelude}/try.tl +11 -4
  21. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/parser/src/ast.rs +1 -1
  22. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/parser/src/parser.rs +0 -1
  23. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/py/emit.rs +2 -6
  24. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/resolve_scopes.rs +138 -44
  25. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/transform.rs +207 -145
  26. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/prelude/functional/__init__.tl +1 -1
  27. koatl-0.1.26/python/koatl/prelude/functional/algebra.tl +46 -0
  28. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/prelude/functional/async.tl +3 -3
  29. {koatl-0.1.24/koatl → koatl-0.1.26}/python/koatl/prelude/functional/list.tl +1 -1
  30. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/prelude/functional/memo.tl +21 -11
  31. {koatl-0.1.24/koatl → koatl-0.1.26}/python/koatl/prelude/functional/reader.tl +2 -2
  32. {koatl-0.1.24/koatl → koatl-0.1.26}/python/koatl/prelude/functional/result.tl +45 -29
  33. {koatl-0.1.24/koatl → koatl-0.1.26}/python/koatl/prelude/iterable.tl +2 -2
  34. {koatl-0.1.24/koatl → koatl-0.1.26}/python/koatl/runtime/__init__.py +16 -7
  35. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/runtime/helpers.py +9 -6
  36. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/runtime/virtual.py +2 -1
  37. koatl-0.1.24/koatl/python/koatl/prelude/functional/monad.tl +0 -20
  38. koatl-0.1.24/koatl/tests/e2e/base/loops.tl +0 -14
  39. koatl-0.1.24/python/koatl/prelude/functional/monad.tl +0 -20
  40. {koatl-0.1.24 → koatl-0.1.26}/Cargo.toml +0 -0
  41. {koatl-0.1.24 → koatl-0.1.26}/README.md +0 -0
  42. {koatl-0.1.24 → koatl-0.1.26}/koatl/.github/workflows/CI.yml +0 -0
  43. {koatl-0.1.24 → koatl-0.1.26}/koatl/.gitignore +0 -0
  44. {koatl-0.1.24 → koatl-0.1.26}/koatl/LICENSE +0 -0
  45. {koatl-0.1.24 → koatl-0.1.26}/koatl/README.md +0 -0
  46. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/__init__.py +0 -0
  47. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/__main__.py +0 -0
  48. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/cli.py +0 -0
  49. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/notebook/__init__.py +0 -0
  50. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/notebook/magic.py +0 -0
  51. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/prelude/__init__.tl +0 -0
  52. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  53. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  54. {koatl-0.1.24 → koatl-0.1.26}/koatl/python/koatl/runtime/record.py +0 -0
  55. {koatl-0.1.24 → koatl-0.1.26}/koatl/requirements.txt +0 -0
  56. {koatl-0.1.24 → koatl-0.1.26}/koatl/src/emit_py.rs +0 -0
  57. {koatl-0.1.24 → koatl-0.1.26}/koatl/src/lib.rs +0 -0
  58. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/containers.tl +0 -0
  59. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/decorators.tl +0 -0
  60. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  61. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/destructure.tl +0 -0
  62. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  63. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/fstr.tl +0 -0
  64. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/functions.tl +0 -0
  65. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/generator.tl +0 -0
  66. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/if_expr.tl +0 -0
  67. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/imports.tl +0 -0
  68. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/nary-list.tl +0 -0
  69. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/placeholder.tl +0 -0
  70. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/precedence.tl +0 -0
  71. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/scopes.tl +0 -0
  72. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  73. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/destructure.tl +0 -0
  74. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/prelude/async.tl +0 -0
  75. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  76. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/prelude/list.tl +0 -0
  77. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/prelude/reader.tl +0 -0
  78. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/prelude/result.tl +0 -0
  79. {koatl-0.1.24/koatl/tests/e2e/base → koatl-0.1.26/koatl/tests/e2e/prelude}/slice.tl +0 -0
  80. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  81. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/util/__init__.py +0 -0
  82. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/util/module0.tl +0 -0
  83. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/util/module1.tl +0 -0
  84. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/e2e/util/module2.tl +0 -0
  85. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/parse/arith.tl +0 -0
  86. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/parse/assign.tl +0 -0
  87. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/parse/block-comments.tl +0 -0
  88. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/parse/deco.tl +0 -0
  89. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/parse/func.tl +0 -0
  90. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/parse/matches.tl +0 -0
  91. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/test_e2e.py +0 -0
  92. {koatl-0.1.24 → koatl-0.1.26}/koatl/tests/test_parse.py +0 -0
  93. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/Cargo.toml +0 -0
  94. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/parser/Cargo.toml +0 -0
  95. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/parser/src/lexer.rs +0 -0
  96. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/parser/src/lib.rs +0 -0
  97. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/parser/src/util.rs +0 -0
  98. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/parser/tests/lexer.rs +0 -0
  99. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/inference.rs +0 -0
  100. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/lib.rs +0 -0
  101. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/main.rs +0 -0
  102. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/parse_timer.rs +0 -0
  103. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/parser.rs +0 -0
  104. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/py/ast.rs +0 -0
  105. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/py/mod.rs +0 -0
  106. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/py/util.rs +0 -0
  107. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/types.rs +0 -0
  108. {koatl-0.1.24 → koatl-0.1.26}/koatl-core/src/util.rs +0 -0
  109. {koatl-0.1.24 → koatl-0.1.26}/pyproject.toml +0 -0
  110. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/__init__.py +0 -0
  111. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/__main__.py +0 -0
  112. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/cli.py +0 -0
  113. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/notebook/__init__.py +0 -0
  114. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/notebook/magic.py +0 -0
  115. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/prelude/__init__.tl +0 -0
  116. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/prelude/functional/async_util.py +0 -0
  117. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/runtime/meta_finder.py +0 -0
  118. {koatl-0.1.24 → koatl-0.1.26}/python/koatl/runtime/record.py +0 -0
@@ -99,7 +99,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
99
99
 
100
100
  [[package]]
101
101
  name = "koatl"
102
- version = "0.1.24"
102
+ version = "0.1.26"
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.24
3
+ Version: 0.1.26
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.24"
3
+ version = "0.1.26"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -1,4 +1,4 @@
1
- export import .monad.*
1
+ export import .algebra.*
2
2
  export import .result.*
3
3
  export import .async.*
4
4
  export import .reader.*
@@ -0,0 +1,46 @@
1
+ import abc
2
+
3
+ export Monad = class(abc.ABC):
4
+ """
5
+ Abstract base class for monads.
6
+
7
+ Subclasses must implement `bind` and `pure`.
8
+ Automatically provides standard monadic operations: bind_once, map, and apply.
9
+ """
10
+ __slots__ = ()
11
+
12
+ bind = abc.abstractmethod& (self, f) => None
13
+
14
+ pure = staticmethod& abc.abstractmethod& value => None
15
+
16
+ # Automatically generated implementations.
17
+ map = (self, f) => self.bind(x => self.pure(f(x)))
18
+ apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
19
+
20
+
21
+ # TODO: should this inherit from Monad?
22
+ export MonadOnce = class(abc.ABC):
23
+ """
24
+ Abstract base class for single-use monads.
25
+
26
+ A MonadOnce can only be bound once, which is suitable for
27
+ use with Python generators that cannot be cloned or reused.
28
+
29
+ Subclasses must implement `bind_once` and `pure`.
30
+ Automatically provides (likely suboptimal) standard monadic operations: bind, map, and apply.
31
+ """
32
+ __slots__ = ()
33
+
34
+ # The default implementation required for `@` syntax that should be overridden by subclasses.
35
+ bind_once = abc.abstractmethod& (self, f) => None
36
+
37
+ pure = staticmethod& abc.abstractmethod& value => None
38
+
39
+ # An optional, optimized implementation of `bind` that skips deep recursion.
40
+ # TODO: can this be automatically generated from bind_once?
41
+ # bind_gen = (self, gen) => ...
42
+
43
+ # Automatically generated implementations.
44
+ bind = (self, f) => self.bind_once(f)
45
+ map = (self, f) => self.bind(x => self.pure(f(x)))
46
+ apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
@@ -2,9 +2,9 @@ import functools.wraps
2
2
  import asyncio
3
3
 
4
4
  import .async_util
5
- import .monad.Monad
5
+ import .MonadOnce
6
6
 
7
- export Async = class(Monad):
7
+ export Async = class(MonadOnce):
8
8
  __init__ = (self, awaitable) => self.generator = awaitable.__await__()
9
9
 
10
10
  __await__ = self => self.generator
@@ -39,5 +39,5 @@ export Async = class(Monad):
39
39
 
40
40
  sleep = staticmethod& x => Async(asyncio.sleep(x))
41
41
 
42
- #- TODO: Why is asyncio.gather eager? -#
42
+ # TODO: Why is asyncio.gather eager?
43
43
  gather = staticmethod& (*args) => Async.from_generator_fn& () => yield from asyncio.gather(*args)
@@ -1,7 +1,7 @@
1
1
  import collections.UserList
2
2
  import itertools
3
3
 
4
- import .monad.Monad
4
+ import .Monad
5
5
  import ..iterable.Iterable
6
6
 
7
7
  export List = class(UserList, Monad):
@@ -1,18 +1,16 @@
1
1
  import collections.defaultdict
2
2
  import functools.wraps
3
3
  import .result.(Result, Ok)
4
- import .Monad
4
+ import .MonadOnce
5
5
 
6
- export Memo = class(Monad):
6
+ export Memo = class(MonadOnce):
7
7
  Cache = class:
8
8
  __init__ = self =>
9
9
  self.cache = defaultdict(dict)
10
10
 
11
11
  __repr__ = self => f"Memo.Cache({self.cache})"
12
12
 
13
- try_get = (self, name, deps) =>
14
- # TODO should try operator return a Result directly?
15
- Result(try self.cache[name][deps] except KeyError())
13
+ try_get = (self, name, deps) => try self.cache[name][deps] except KeyError()
16
14
 
17
15
  update = (self, name, deps, value) =>
18
16
  self.cache[name][deps] = value
@@ -22,6 +20,17 @@ export Memo = class(Monad):
22
20
 
23
21
  __repr__ = self => f"Memo(...)"
24
22
 
23
+ # This needs to be special cased because if we try to use Memo.value
24
+ # directly, the *generator* itself gets memoized. We want to memoize
25
+ # the thing that that comes out of the generator.
26
+ bind_value = staticmethod& (id, deps, f) =>
27
+ Memo(ctx =>
28
+ if ctx.try_get(id, tuple(deps)) matches Ok(value):
29
+ return value
30
+
31
+ ctx.update(id, tuple(deps), f().f(ctx))
32
+ )
33
+
25
34
  value = staticmethod& (id, deps, f) =>
26
35
  Memo(ctx =>
27
36
  if ctx.try_get(id, tuple(deps)) matches Ok(value):
@@ -40,7 +49,7 @@ export Memo = class(Monad):
40
49
 
41
50
  let v = f(*args, **kwargs)
42
51
  if v matches Memo():
43
- v = v.run(ctx)
52
+ v = v.f(ctx)
44
53
 
45
54
  ctx.update(id, deps, v)
46
55
  )
@@ -53,21 +62,22 @@ export Memo = class(Monad):
53
62
  pure = staticmethod& value => Memo(ctx => value)
54
63
 
55
64
  bind_once = (self, f) => Memo(ctx =>
56
- let value = f(self.run(ctx))
65
+ let value = f(self.f(ctx))
57
66
  if value matches Memo():
58
- value = value.run(ctx)
67
+ value = value.f(ctx)
59
68
  value
60
69
  )
61
70
 
62
71
  bind_gen = (self, gen) => Memo(ctx =>
63
- self = self.run(ctx)
72
+ self = self.f(ctx)
64
73
  try:
65
74
  while True:
66
75
  self = gen.send(self)
67
76
  if self matches Memo():
68
- self = self.run(ctx)
77
+ self = self.f(ctx)
69
78
  except StopIteration(value=value):
70
79
  return value
71
80
  )
72
81
 
73
- __tl__.memo = Memo.value
82
+ __tl__.memo_value = Memo.value
83
+ __tl__.bind_memo_value = Memo.bind_value
@@ -1,7 +1,7 @@
1
1
  import functools.wraps
2
- import .monad.Monad
2
+ import .MonadOnce
3
3
 
4
- export Reader = class(Monad):
4
+ export Reader = class(MonadOnce):
5
5
  __init__ = (self, fn) => self.fn = fn
6
6
 
7
7
  __repr__ = self => "Reader(...)"
@@ -1,8 +1,8 @@
1
1
  import functools.wraps
2
- import .monad.Monad
3
2
  import koatl.runtime.virtual.register_global_attr
3
+ import .algebra.MonadOnce
4
4
 
5
- export Result = class:
5
+ export Result = class(MonadOnce):
6
6
  __slots__ = ()
7
7
  __match_args__ = ("value",)
8
8
 
@@ -14,24 +14,15 @@ export Result = class:
14
14
  __init__ = (self, *args, **kwargs) =>
15
15
  raise ValueError("Result should not be instantiated directly, use Ok or Err")
16
16
 
17
- raw = self =>
17
+ checked = staticmethod& f =>
18
18
  """
19
- Unwraps the value from a Result, making it look like
20
- a non-monadic Python value.
19
+ Runs the function in a try block and returns a Result.
20
+ If the function raises an exception, it will be wrapped in an Err.
21
21
  """
22
- if self matches not Result():
23
- return self
24
-
25
- if self.ok:
26
- if self.value.ok:
27
- return self.value
28
- else:
29
- return self
30
- else:
31
- if self.value.ok:
32
- return self
33
- else:
34
- return self.value
22
+ try:
23
+ return Ok(f())
24
+ except BaseException() as e:
25
+ return Err(e)
35
26
 
36
27
  bind_once = (self, f) =>
37
28
  self = Result(self)
@@ -96,8 +87,14 @@ export Ok = class(Result):
96
87
  return None
97
88
  self.value = value
98
89
 
90
+ __eq__ = (self, other) =>
91
+ if not isinstance(other, Ok):
92
+ return False
93
+ return self.value == other.value
94
+
99
95
  __repr__ = self => f"Ok({repr(self.value)})"
100
96
  unwrap = self => self.value
97
+ coalesce = (self, f) => self.value
101
98
 
102
99
  export Err = class(Result):
103
100
  __slots__ = ("value",)
@@ -108,24 +105,43 @@ export Err = class(Result):
108
105
  return None
109
106
  self.value = value
110
107
 
108
+ __eq__ = (self, other) =>
109
+ if not isinstance(other, Err):
110
+ return False
111
+ return self.value == other.value
112
+
111
113
  __repr__ = self => f"Err({repr(self.value)})"
112
114
  unwrap = self =>
113
115
  if self.value matches BaseException():
114
116
  raise self.value
115
117
  raise ValueError(f"Expected Ok, got {repr(self.value)}")
118
+ coalesce = (self, f) => f()
119
+
120
+
121
+ register_global_attr(type(None), "ok", ExtensionProperty(_ => False))
122
+ register_global_attr(BaseException, "ok", ExtensionProperty(_ => False))
123
+ register_global_attr(object, "ok", ExtensionProperty(_ => True))
124
+
125
+ register_global_attr(object, "result", ExtensionProperty(Result))
126
+
127
+ # Enables @ operator for bare objects.
128
+ register_global_attr(object, "bind_gen", Result.bind_gen)
116
129
 
130
+ __tl__.Ok = Ok
131
+ __tl__.Err = Err
117
132
 
118
- # Provide a default implementation for *most* Result methods
119
- # ...we shouldn't provide a default for `map` since that
120
- # would prevent virtual trait lookup. TODO: fix this?
121
- for name, method in Result.__dict__:
122
- if name.startswith("_"):
123
- continue
124
- if name == "map":
125
- continue
133
+ __tl__.op_coal = (x, f) =>
134
+ if x matches Result():
135
+ return x.coalesce(f)
126
136
 
127
- let make_closure = method => (self, *args, **kwargs) => Result.raw(method(self, *args, **kwargs))
137
+ if not x.ok:
138
+ return f()
139
+ return x
128
140
 
129
- register_global_attr(object, name, make_closure(method))
141
+ __tl__.op_map = (x, f) =>
142
+ if x matches Result():
143
+ return x.map(f)
130
144
 
131
- register_global_attr(object, "ok", ExtensionProperty(__tl__.ok))
145
+ if x.ok:
146
+ return f(x)
147
+ return x
@@ -21,13 +21,13 @@ methods = {
21
21
  if not hasattr(m, "apply"):
22
22
  # special case for bare types - slightly more efficient
23
23
  # ...also required since bare types don't have .map
24
- if not __tl__.ok(m):
24
+ if not m.ok:
25
25
  return m
26
26
 
27
27
  let acc = [m]
28
28
  for v in it:
29
29
  let fv = f(v)
30
- if not __tl__.ok(fv):
30
+ if not fv.ok:
31
31
  return fv
32
32
  acc.append(fv)
33
33
  return acc
@@ -21,10 +21,13 @@ from .record import *
21
21
  from .helpers import *
22
22
 
23
23
 
24
- def dummy_memo(*args, **kwargs):
25
- raise RuntimeError(
26
- "memo is not available without the prelude. Please import koatl.prelude."
27
- )
24
+ def dummy(name):
25
+ def wrapper(*args, **kwargs):
26
+ raise RuntimeError(
27
+ f"{name} is not available without the prelude. Please import koatl.prelude."
28
+ )
29
+
30
+ return wrapper
28
31
 
29
32
 
30
33
  __tl__ = SimpleNamespace(
@@ -32,15 +35,21 @@ __tl__ = SimpleNamespace(
32
35
  slice=slice,
33
36
  vget=virtual.vget,
34
37
  vhas=virtual.vhas,
35
- memo=dummy_memo,
36
38
  unpack_record=helpers.unpack_record,
37
39
  set_exports=helpers.set_exports,
38
40
  do=helpers.do,
39
- ok=helpers.ok,
40
41
  partial=functools.partial,
42
+ # These require more complex logic and require the prelude.
43
+ # The runtime provides dummy implementations that raise if used without the prelude.
44
+ memo_value=dummy("memo"),
45
+ bind_memo_value=dummy("memo"),
46
+ op_map=dummy("?"),
47
+ op_coal=("??"),
48
+ Ok=dummy("try-expr"),
49
+ Err=dummy("try-expr"),
41
50
  **{name: helpers.__dict__[name] for name in helpers.__all__},
42
51
  **{name: record.__dict__[name] for name in record.__all__},
43
- **{name: virtual.__dict__[name] for name in virtual.__all__}
52
+ **{name: virtual.__dict__[name] for name in virtual.__all__},
44
53
  )
45
54
 
46
55
 
@@ -66,6 +66,10 @@ def do(f):
66
66
  try:
67
67
  m = gen.send(None)
68
68
  except StopIteration as e:
69
+ if not hasattr(e.value, "bind_once"):
70
+ raise ValueError(
71
+ "This do-block returned a bare value before it could infer the monadic type. Wrap the value in pure()."
72
+ ) from None
69
73
  return e.value
70
74
 
71
75
  def recurse(v):
@@ -74,17 +78,16 @@ def do(f):
74
78
  m = gen.send(v)
75
79
  return vget(m, "bind_once")(recurse)
76
80
  except StopIteration as e:
77
- try:
78
- return vget(m, "pure")(e.value)
79
- except AttributeError:
80
- return e.value
81
+ return vget(m, "pure")(e.value)
81
82
 
82
83
  try:
83
84
  # TODO: this is a workaround to avoid recursion.
84
85
  # is it possible to derive bind_gen directly from bind_once?
85
86
 
86
- return vget(m, "bind_gen")(gen)
87
- except (NotImplementedError, AttributeError):
87
+ bind_gen = vget(m, "bind_gen")
88
+ except AttributeError:
88
89
  return vget(m, "bind_once")(recurse)
89
90
 
91
+ return bind_gen(gen)
92
+
90
93
  return impl
@@ -1,3 +1,4 @@
1
+ import abc
1
2
  from functools import partial
2
3
  from itertools import count
3
4
  from types import new_class
@@ -105,7 +106,7 @@ def Trait(module, name, methods, *, requires=[]):
105
106
 
106
107
  meta = type(
107
108
  f"{name}Meta",
108
- (type,),
109
+ (abc.ABCMeta,),
109
110
  {"__instancecheck__": instancecheck, "__module__": "types"},
110
111
  )
111
112
 
@@ -17,15 +17,3 @@ assert_eq(1?.($ + 1)(), 2)
17
17
 
18
18
  assert_eq(None ?? 1, 1)
19
19
  assert_eq(int(5) ?? 1, 5)
20
-
21
-
22
- # try exprs
23
-
24
- assert_eq(type(try x), NameError)
25
- assert_eq(type(try int(1)[1]), TypeError)
26
- assert_eq(type(try [0][5]), IndexError)
27
- assert_eq(try 5, 5)
28
-
29
-
30
- assert_eq((try 5)?.($+1)?(), 6)
31
- assert_eq(type((try x)?.($+1)?()), NameError)
@@ -0,0 +1,25 @@
1
+ import util.assert_eq
2
+
3
+ x = []
4
+ for i in [1, 2, 3]:
5
+ x = [*x, i]
6
+
7
+ assert_eq(x, [1, 2, 3])
8
+
9
+ i = True
10
+ while (x => x)(i):
11
+ if i === False:
12
+ assert False
13
+
14
+ i = False
15
+
16
+ counts = []
17
+ counter = 0
18
+ while (
19
+ counter = counter + 1
20
+ counter < 5
21
+ ):
22
+ counts.append(counter)
23
+
24
+ assert_eq(counter, 5)
25
+ assert_eq(counts, [1, 2, 3, 4])
@@ -73,8 +73,6 @@ x = 1
73
73
  assert_eq([1, 2, 3] matches [1, 2, 3], True)
74
74
  assert_eq([1, 2, 3] matches [.x, _, 3], True)
75
75
 
76
- assert_eq(try 1 matches 1, True)
77
-
78
76
  f = () =>
79
77
  if [1, 2, 3] matches [1, 2, x]:
80
78
  assert_eq(x, 3)
@@ -0,0 +1,42 @@
1
+ import util.assert_eq
2
+
3
+ assert_eq(
4
+ True and False
5
+ False
6
+ )
7
+
8
+ # No short-circuiting here.
9
+
10
+ flag = 0
11
+ assert_eq(
12
+ True and (
13
+ flag = 1
14
+ True
15
+ )
16
+ True
17
+ )
18
+ assert_eq(flag, 1)
19
+
20
+ # This needs to short-circuit.
21
+
22
+ flag = 0
23
+ assert_eq(
24
+ False and (
25
+ flag = 1
26
+ True
27
+ )
28
+ False
29
+ )
30
+ assert_eq(flag, 0)
31
+
32
+ # Same with or
33
+
34
+ flag = 0
35
+ assert_eq(
36
+ True or (
37
+ flag = 1
38
+ True
39
+ )
40
+ True
41
+ )
42
+ assert_eq(flag, 0)
@@ -4,7 +4,7 @@ counts = 0
4
4
 
5
5
  fib = Memo.fn& x =>
6
6
  if x < 2:
7
- return 1
7
+ return Memo.pure(1)
8
8
 
9
9
  counts = counts + 1
10
10
 
@@ -3,12 +3,14 @@ import util.assert_eq
3
3
  # precedence
4
4
  assert_eq(try 1 ?? 2, 1)
5
5
  assert_eq(try z ?? 2, 2)
6
- assert_eq(type(try z), NameError)
6
+ assert_eq(try z matches Err(), True)
7
+ assert_eq((try z).value matches NameError(), True)
7
8
  assert_eq(try z ?? try 1 ?? 2, 1)
8
- assert_eq(try z ?? try None ?? 2, 2)
9
+ assert_eq(try z ?? try None ?? 2, None)
10
+ assert_eq(try z ?? Err(None) ?? 2, 2)
9
11
 
10
12
  err_type = NameError
11
- assert_eq(type(try a except err_type()), err_type)
13
+ assert_eq(try a except err_type() matches Err(), True)
12
14
 
13
15
  # handlers
14
16
  assert_eq(try 1 except ValueError() ?? 2, 1)
@@ -19,4 +21,9 @@ try:
19
21
  try a except (ValueError() | StopIteration())
20
22
  assert False
21
23
  except:
22
- None
24
+ None
25
+
26
+ assert_eq(try 1 matches Ok(1), True)
27
+
28
+ assert_eq((try 5)?.($+1)?(), Ok(6))
29
+ assert_eq((try x)?.($+1)?() matches Err(), True)
@@ -30,7 +30,7 @@ impl<T> IntoIndirect<T> for T {
30
30
  }
31
31
  }
32
32
 
33
- #[derive(Debug, Copy, Clone)]
33
+ #[derive(Debug, Copy, Clone, PartialEq, Eq)]
34
34
  pub enum BinaryOp {
35
35
  Add,
36
36
  Sub,
@@ -210,7 +210,6 @@ where
210
210
  if items.len() == 1 && last_comma.is_none() {
211
211
  let item = items.into_iter().next().unwrap();
212
212
  if let PatternSequenceItem::Item(inner) = item {
213
- // TODO ???
214
213
  inner.extract()
215
214
  } else {
216
215
  Pattern::Sequence(vec![item]).spanned(e.span())
@@ -456,21 +456,18 @@ impl SPyExpr<'_> {
456
456
  ctx.emit("]");
457
457
  }
458
458
  PyExpr::Yield(expr) => {
459
- set_prec(-0.5);
460
459
  ctx.emit("(");
461
460
  ctx.emit("yield ");
462
461
  expr.emit_to(ctx, LOW_PREC)?;
463
462
  ctx.emit(")");
464
463
  }
465
464
  PyExpr::Await(expr) => {
466
- set_prec(-0.5);
467
- ctx.emit_indent();
465
+ ctx.emit("(");
468
466
  ctx.emit("await ");
469
467
  expr.emit_to(ctx, LOW_PREC)?;
470
- ctx.emit_endl();
468
+ ctx.emit(")");
471
469
  }
472
470
  PyExpr::YieldFrom(expr) => {
473
- set_prec(-0.5); // TODO is this precedence correct?
474
471
  ctx.emit("(");
475
472
  ctx.emit("yield from ");
476
473
  expr.emit_to(ctx, LOW_PREC)?;
@@ -511,7 +508,6 @@ impl SPyExpr<'_> {
511
508
  }
512
509
  }
513
510
  PyExpr::IfExpr(cond, if_, else_) => {
514
- // TODO precedence here?
515
511
  ctx.emit("(");
516
512
  if_.emit_to(ctx, LOW_PREC)?;
517
513
  ctx.emit(" if ");