koatl 0.1.21__tar.gz → 0.1.23__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 (116) hide show
  1. {koatl-0.1.21 → koatl-0.1.23}/Cargo.lock +1 -1
  2. {koatl-0.1.21 → koatl-0.1.23}/PKG-INFO +1 -1
  3. {koatl-0.1.21 → koatl-0.1.23}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/__init__.tl +16 -3
  5. koatl-0.1.23/koatl/python/koatl/prelude/functional/list.tl +26 -0
  6. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/monad.tl +10 -4
  7. {koatl-0.1.21 → koatl-0.1.23/koatl}/python/koatl/prelude/functional/result.tl +38 -41
  8. koatl-0.1.23/koatl/python/koatl/prelude/iterable.tl +83 -0
  9. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/helpers.py +1 -1
  10. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/virtual.py +2 -2
  11. {koatl-0.1.21 → koatl-0.1.23}/koatl/src/lib.rs +23 -8
  12. koatl-0.1.23/koatl/tests/e2e/prelude/list.tl +18 -0
  13. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/parser.rs +11 -1
  14. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/resolve_scopes.rs +0 -14
  15. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/transform.rs +22 -8
  16. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/__init__.tl +16 -3
  17. koatl-0.1.23/python/koatl/prelude/functional/list.tl +26 -0
  18. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/monad.tl +10 -4
  19. {koatl-0.1.21/koatl → koatl-0.1.23}/python/koatl/prelude/functional/result.tl +38 -41
  20. koatl-0.1.23/python/koatl/prelude/iterable.tl +83 -0
  21. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/runtime/helpers.py +1 -1
  22. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/runtime/virtual.py +2 -2
  23. koatl-0.1.21/koatl/python/koatl/prelude/iterable.tl +0 -56
  24. koatl-0.1.21/python/koatl/prelude/iterable.tl +0 -56
  25. {koatl-0.1.21 → koatl-0.1.23}/Cargo.toml +0 -0
  26. {koatl-0.1.21 → koatl-0.1.23}/README.md +0 -0
  27. {koatl-0.1.21 → koatl-0.1.23}/koatl/.github/workflows/CI.yml +0 -0
  28. {koatl-0.1.21 → koatl-0.1.23}/koatl/.gitignore +0 -0
  29. {koatl-0.1.21 → koatl-0.1.23}/koatl/LICENSE +0 -0
  30. {koatl-0.1.21 → koatl-0.1.23}/koatl/README.md +0 -0
  31. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/__init__.py +0 -0
  32. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/__main__.py +0 -0
  33. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/cli.py +0 -0
  34. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/notebook/__init__.py +0 -0
  35. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/notebook/magic.py +0 -0
  36. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/__init__.tl +0 -0
  37. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/async.tl +0 -0
  38. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  39. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/memo.tl +0 -0
  40. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
  41. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/__init__.py +0 -0
  42. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  43. {koatl-0.1.21 → koatl-0.1.23}/koatl/python/koatl/runtime/record.py +0 -0
  44. {koatl-0.1.21 → koatl-0.1.23}/koatl/requirements.txt +0 -0
  45. {koatl-0.1.21 → koatl-0.1.23}/koatl/src/emit_py.rs +0 -0
  46. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/coal.tl +0 -0
  47. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/containers.tl +0 -0
  48. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/decorators.tl +0 -0
  49. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  50. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/destructure.tl +0 -0
  51. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  52. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/fstr.tl +0 -0
  53. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/functions.tl +0 -0
  54. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/generator.tl +0 -0
  55. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/if_expr.tl +0 -0
  56. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/imports.tl +0 -0
  57. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/loops.tl +0 -0
  58. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/match.tl +0 -0
  59. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/nary-list.tl +0 -0
  60. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/placeholder.tl +0 -0
  61. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/precedence.tl +0 -0
  62. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/scopes.tl +0 -0
  63. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  64. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/slice.tl +0 -0
  65. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/base/try.tl +0 -0
  66. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/destructure.tl +0 -0
  67. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/async.tl +0 -0
  68. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  69. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/memo.tl +0 -0
  70. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/reader.tl +0 -0
  71. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/result.tl +0 -0
  72. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  73. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/util/__init__.py +0 -0
  74. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/util/module0.tl +0 -0
  75. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/util/module1.tl +0 -0
  76. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/e2e/util/module2.tl +0 -0
  77. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/arith.tl +0 -0
  78. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/assign.tl +0 -0
  79. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/block-comments.tl +0 -0
  80. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/deco.tl +0 -0
  81. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/func.tl +0 -0
  82. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/parse/matches.tl +0 -0
  83. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/test_e2e.py +0 -0
  84. {koatl-0.1.21 → koatl-0.1.23}/koatl/tests/test_parse.py +0 -0
  85. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/Cargo.toml +0 -0
  86. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/Cargo.toml +0 -0
  87. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/ast.rs +0 -0
  88. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/lexer.rs +0 -0
  89. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/lib.rs +0 -0
  90. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/src/util.rs +0 -0
  91. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/parser/tests/lexer.rs +0 -0
  92. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/inference.rs +0 -0
  93. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/lib.rs +0 -0
  94. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/main.rs +0 -0
  95. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/parse_timer.rs +0 -0
  96. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/parser.rs +0 -0
  97. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/py/ast.rs +0 -0
  98. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/py/emit.rs +0 -0
  99. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/py/mod.rs +0 -0
  100. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/py/util.rs +0 -0
  101. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/types.rs +0 -0
  102. {koatl-0.1.21 → koatl-0.1.23}/koatl-core/src/util.rs +0 -0
  103. {koatl-0.1.21 → koatl-0.1.23}/pyproject.toml +0 -0
  104. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/__init__.py +0 -0
  105. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/__main__.py +0 -0
  106. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/cli.py +0 -0
  107. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/notebook/__init__.py +0 -0
  108. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/notebook/magic.py +0 -0
  109. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/__init__.tl +0 -0
  110. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/async.tl +0 -0
  111. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/async_util.py +0 -0
  112. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/memo.tl +0 -0
  113. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/prelude/functional/reader.tl +0 -0
  114. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/runtime/__init__.py +0 -0
  115. {koatl-0.1.21 → koatl-0.1.23}/python/koatl/runtime/meta_finder.py +0 -0
  116. {koatl-0.1.21 → koatl-0.1.23}/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.21"
102
+ version = "0.1.23"
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.21
3
+ Version: 0.1.23
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.21"
3
+ version = "0.1.23"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -3,9 +3,17 @@ export import .result.*
3
3
  export import .async.*
4
4
  export import .reader.*
5
5
  export import .memo.*
6
+ export import .list.*
6
7
 
7
- export Fn = class:
8
- compose = staticmethod& (*args) =>
8
+ named = name => fn =>
9
+ fn.__name__ = name
10
+ fn.__qualname__ = name
11
+ fn
12
+
13
+ methods = {
14
+ id: named("id")& x => x
15
+
16
+ compose: named("compose")& (*args) =>
9
17
  args match:
10
18
  [] => raise ValueError("At least one function is required for composition")
11
19
  [f] => f
@@ -21,4 +29,9 @@ export Fn = class:
21
29
  composed.signature = fs[-1].signature
22
30
 
23
31
  composed
24
- default raise ValueError("Invalid arguments for Fn.compose()")
32
+ default raise ValueError("Invalid arguments for Fn.compose()")
33
+ }
34
+
35
+ globals().update(methods)
36
+
37
+ __all__ = methods.keys() | tuple
@@ -0,0 +1,26 @@
1
+ import collections.UserList
2
+ import itertools
3
+
4
+ import .monad.Monad
5
+ import ..iterable.Iterable
6
+
7
+ export List = class(UserList, Monad):
8
+ bind_once = (self, f) =>
9
+ raise NotImplementedError(
10
+ "Binding List in a do-block doesn't work in Koatl " +
11
+ "due to generator limitations. " +
12
+ "Wrap in Ok to use the regular Result monad."
13
+ )
14
+
15
+ bind = (self, f) => List(self.flat_map(f))
16
+
17
+ pure = staticmethod& x => [x]
18
+
19
+ traverse = Iterable.traverse
20
+
21
+
22
+ for name, method in List.__dict__:
23
+ if name.startswith("_"):
24
+ continue
25
+
26
+ register_global_attr(list, name, method)
@@ -1,12 +1,18 @@
1
1
  import abc
2
2
 
3
3
  export Monad = class(abc.ABC):
4
+ # The default implementation required for `@` syntax that should be overridden by subclasses.
5
+ bind_once = abc.abstractmethod& (self, f) => None
6
+
7
+ pure = staticmethod& abc.abstractmethod& value => None
8
+
9
+ # Automatically given by bind_once.
4
10
  bind = (self, f) => self.bind_once(f)
5
11
 
6
12
  # An optional, optimized implementation of `bind` that skips deep recursion.
13
+ # TODO: can this be automatically generated?
7
14
  bind_gen = (self, gen) => raise NotImplementedError()
8
15
 
9
- # The default implementation required for `@` syntax that should be overridden by subclasses.
10
- bind_once = abc.abstractmethod& (self, f) => None
11
-
12
- pure = staticmethod& abc.abstractmethod& value => None
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)))
@@ -8,6 +8,8 @@ export Result = class(Monad):
8
8
  Ok() => f(self)
9
9
  default => self
10
10
 
11
+ bind = bind_once
12
+
11
13
  bind_gen = (self, gen) =>
12
14
  try:
13
15
  while True:
@@ -23,6 +25,28 @@ export Result = class(Monad):
23
25
  return Ok(None)
24
26
  return value
25
27
 
28
+ pure = staticmethod& x => Ok(x)
29
+
30
+ apply = (self, f) =>
31
+ if not __tl__.ok(self):
32
+ return self
33
+ if not __tl__.ok(f):
34
+ return f
35
+
36
+ # unwrap the Oks - this is a bit weird
37
+ # but it allows bare types to be Results
38
+ if self matches Ok(value):
39
+ self = self.value
40
+ if f matches Ok(value):
41
+ f = f.value
42
+
43
+ f(self)
44
+
45
+ # Map must not be defined since it will override any special map
46
+ # implementation for traits, specifically for Iterable -
47
+ # is there a better way around this (in the vtable implementation)?
48
+ # map = ...
49
+
26
50
  map_err = (self, f) =>
27
51
  if self matches Err():
28
52
  return f(self)
@@ -44,50 +68,23 @@ export Ok = class(metaclass=OkMeta):
44
68
  __init__ = (self, value) =>
45
69
  self.value = value
46
70
 
47
- # Since we are using a custom metaclass, we can't derive from Result
48
- # so need to copy the methods over manually from Result
49
-
50
- # We could just use the object fallback methods, but this avoids
51
- # the manual __tl__.vget so is slightly faster
52
- bind_once = Result.bind_once
53
- bind_gen = Result.bind_gen
54
- map_err = Result.map_err
55
- map_none = Result.map_none
56
-
57
71
  assert = staticmethod& value =>
58
72
  value match:
59
73
  BaseException() as e => raise e
60
74
  None => raise ValueError("Expected a value, got None")
61
75
  default value
62
76
 
63
- export Err = BaseException
64
-
65
- register_global_attr(
66
- object,
67
- "map_err",
68
- Result.map_err
69
- )
70
-
71
- register_global_attr(
72
- object,
73
- "map_none",
74
- Result.map_none
75
- )
76
-
77
- register_global_attr(
78
- object,
79
- "bind",
80
- Result.bind_once
81
- )
82
-
83
- register_global_attr(
84
- object,
85
- "bind_once",
86
- Result.bind_once
87
- )
88
-
89
- register_global_attr(
90
- object,
91
- "bind_gen",
92
- Result.bind_gen
93
- )
77
+
78
+ # Since we are using a custom metaclass, we can't derive from Result
79
+ # so need to copy the methods over manually from Result
80
+
81
+ # We could just use the object fallback methods, but this avoids
82
+ # the vget so is slightly faster
83
+ for name, method in Result.__dict__:
84
+ if name.startswith("_"):
85
+ continue
86
+
87
+ setattr(Ok, name, method)
88
+ register_global_attr(object, name, method)
89
+
90
+ export Err = BaseException
@@ -0,0 +1,83 @@
1
+ import itertools
2
+ import builtins
3
+
4
+ methods = {
5
+ map: (x, f) => builtins.map(f, x.iter)
6
+
7
+ filter: (x, f) => builtins.filter(f, x.iter)
8
+
9
+ flat_map: (x, f) => itertools.chain.from_iterable(x.map(f))
10
+
11
+ traverse: (x, f) =>
12
+ let it = x.iter
13
+ let v
14
+ try:
15
+ v = next(it)
16
+ except StopIteration():
17
+ return []
18
+
19
+ let m = f(v)
20
+
21
+ if not hasattr(m, "apply"):
22
+ # special case for bare types - slightly more efficient
23
+ # ...also required since bare types don't have .map
24
+ if not __tl__.ok(m):
25
+ return m
26
+
27
+ let acc = [m]
28
+ for v in it:
29
+ let fv = f(v)
30
+ if not __tl__.ok(fv):
31
+ return fv
32
+ acc.append(fv)
33
+ return acc
34
+
35
+ let acc = m.map([$])
36
+ for v in it:
37
+ m = f(v)
38
+ # acc = liftA2((list, value) => [*list, value])(acc, f(v))
39
+ acc = acc.apply(m.map(x => acc => [*acc, x]))
40
+ acc
41
+
42
+ fold: (x, init, f) =>
43
+ let acc = init
44
+ for i in x:
45
+ acc = f(acc, i)
46
+ acc
47
+
48
+ first: (x, f) =>
49
+ for i in x:
50
+ if f(i):
51
+ return i
52
+ return None
53
+
54
+ last: (x, f) =>
55
+ let result = None
56
+ for i in x:
57
+ if f(i):
58
+ result = i
59
+ return result
60
+
61
+ at: (x, index) =>
62
+ for i in x:
63
+ if i == index:
64
+ return i
65
+ raise IndexError("Index out of range")
66
+
67
+ sum: x =>
68
+ let acc = 0
69
+ for i in x:
70
+ acc = acc + i
71
+ acc
72
+
73
+ list: x =>
74
+ list(x.iter)
75
+
76
+ record: x =>
77
+ Record(x.iter)
78
+ }
79
+
80
+ # TODO merge this with typing.Iterable?
81
+
82
+ export Iterable = Trait(__name__, "Iterable", methods, requires=["iter"])
83
+ register_global_trait(Iterable)
@@ -76,7 +76,7 @@ def do(f):
76
76
 
77
77
  try:
78
78
  # TODO: this is a workaround to avoid recursion.
79
- # is it possible to get bind_gen directly from bind_once?
79
+ # is it possible to derive bind_gen directly from bind_once?
80
80
 
81
81
  return vget(m, "bind_gen")(gen)
82
82
  except (NotImplementedError, AttributeError):
@@ -9,7 +9,7 @@ def ExtensionProperty(f):
9
9
  return f
10
10
 
11
11
 
12
- def vget(obj, name):
12
+ def vget(obj, name, ignore_traits=False):
13
13
  try:
14
14
  return getattr(obj, name)
15
15
  except:
@@ -34,7 +34,7 @@ def vget(obj, name):
34
34
  except TypeError:
35
35
  pass
36
36
 
37
- v = fast_vget(obj, name)
37
+ v = fast_vget(obj, name, ignore_traits)
38
38
  if v is not None:
39
39
  if hasattr(v, "ext_prop"):
40
40
  return v(obj)
@@ -79,27 +79,42 @@ struct TraitAttr {
79
79
  static VTBL2: Lazy<Mutex<HashMap<String, Vec<TraitAttr>>>> =
80
80
  Lazy::new(|| Mutex::new(HashMap::new()));
81
81
 
82
- #[pyfunction(signature=(obj, name))]
82
+ #[pyfunction(signature=(obj, name, ignore_traits))]
83
83
  fn fast_vget<'py, 'ptr>(
84
84
  obj: &'ptr Bound<'py, PyAny>,
85
85
  name: &'ptr Bound<'py, PyString>,
86
+ ignore_traits: bool,
86
87
  ) -> PyResult<PyObject> {
87
88
  let name = name.to_string();
88
89
 
89
90
  let py = obj.py();
90
91
 
91
- let vtbl = VTBL.lock().unwrap();
92
+ {
93
+ // This block is necessary to ensure the lock is released
94
+ // before we possibly re-enter fast_vget through __tl__.vget below.
92
95
 
93
- if let Some(types) = vtbl.get(&name) {
94
- let mro = obj.get_type().mro();
96
+ let vtbl = VTBL.lock().unwrap();
95
97
 
96
- for typ in mro {
97
- if let Some(attr) = types.get(&(typ.as_ptr() as usize)) {
98
- return Ok((*attr).clone_ref(py));
98
+ if let Some(types) = vtbl.get(&name) {
99
+ let mro = obj.get_type().mro();
100
+
101
+ for typ in mro {
102
+ if let Some(attr) = types.get(&(typ.as_ptr() as usize)) {
103
+ return Ok((*attr).clone_ref(py));
104
+ }
99
105
  }
100
106
  }
101
107
  }
102
108
 
109
+ if ignore_traits {
110
+ // This prevents a deadlock from fast_vget being called
111
+ // *again* from inside __tl_.vget while vtbl2 is locked.
112
+
113
+ // Also, prevents a trait from being dependent on other traits,
114
+ // which might cause an infinite loop sometimes.
115
+ return Ok(py.None().into_any());
116
+ }
117
+
103
118
  let vtbl2 = VTBL2.lock().unwrap();
104
119
 
105
120
  if let Some(traits) = vtbl2.get(&name) {
@@ -109,7 +124,7 @@ fn fast_vget<'py, 'ptr>(
109
124
  for t in traits {
110
125
  let mut ok = true;
111
126
  for r in t.requirements.iter() {
112
- if vget.call1((obj, r)).is_err() {
127
+ if vget.call1((obj, r, true)).is_err() {
113
128
  ok = false;
114
129
  break;
115
130
  }
@@ -0,0 +1,18 @@
1
+ import util.assert_eq
2
+
3
+ # Traverse makes sure that apply and map implementations are correct.
4
+
5
+ assert_eq(
6
+ [1, 2, 3].traverse(x => @Async.pure(x)).run() # run in a new event loop
7
+ [1, 2, 3]
8
+ )
9
+
10
+ assert_eq(
11
+ [1, 2, 3].traverse(x => x + 1)
12
+ [2, 3, 4]
13
+ )
14
+
15
+ assert_eq(
16
+ [1, 2, 3].traverse(x => None)
17
+ None
18
+ )
@@ -640,7 +640,17 @@ where
640
640
  .or_not()
641
641
  .then_ignore(just(Token::Kw("import")))
642
642
  .then(group((
643
- symbol(".").repeated().count(),
643
+ // TODO this looks extremely cursed
644
+ symbol("..")
645
+ .to(2)
646
+ .repeated()
647
+ .foldr(
648
+ symbol(".")
649
+ .or_not()
650
+ .map(|x| if x.is_some() { 1 } else { 0 }),
651
+ |acc, sum| acc + sum,
652
+ )
653
+ .boxed(),
644
654
  ident
645
655
  .clone()
646
656
  .then_ignore(symbol("."))
@@ -213,13 +213,6 @@ impl<'src> ResolveState<'src> {
213
213
  let placeholder_ctx = self.placeholder_stack.pop().unwrap();
214
214
 
215
215
  if placeholder_ctx.activated {
216
- if ph_fn_ctx.is_async || ph_fn_ctx.is_generator || ph_fn_ctx.is_do {
217
- self.errors.extend(simple_err(
218
- "Await, bind, and yield are not allowed in placeholder functions",
219
- placeholder_ctx.span,
220
- ));
221
- }
222
-
223
216
  ph_fn_ctx.is_placeholder = true;
224
217
 
225
218
  ph_fn_ctx.arg_names.push(placeholder_ctx.arg_decl);
@@ -1323,13 +1316,6 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
1323
1316
 
1324
1317
  let fn_ctx = state.fn_stack.pop().unwrap();
1325
1318
 
1326
- if fn_ctx.is_async || fn_ctx.is_generator || fn_ctx.is_do {
1327
- state.errors.extend(simple_err(
1328
- "Memo expressions cannot be async, generator, or do",
1329
- span,
1330
- ));
1331
- }
1332
-
1333
1319
  state.memo_captures.insert(inner.as_ref().into(), fn_ctx);
1334
1320
 
1335
1321
  Expr::Memo(inner)
@@ -15,6 +15,7 @@ use crate::{
15
15
  use once_cell::sync::Lazy;
16
16
  use parser::ast::*;
17
17
  use slotmap::SlotMap;
18
+ use std::hash::{Hash, Hasher};
18
19
 
19
20
  static PY_KWS: &[&str] = &[
20
21
  "and", "as", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except",
@@ -2279,20 +2280,26 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for SExpr<'src> {
2279
2280
 
2280
2281
  let callback = pre.bind(make_fn_exp(
2281
2282
  ctx,
2282
- FnDef::PyFnDef(vec![], py_body, false, false),
2283
+ FnDef::PyFnDef(vec![], py_body, memo_captures.is_do, memo_captures.is_async),
2283
2284
  &span,
2284
2285
  )?);
2285
2286
 
2286
2287
  let linecol = ctx.line_cache.linecol(span.start);
2287
2288
 
2288
- a.yield_(a.call(
2289
+ let memo_call = a.call(
2289
2290
  a.tl_builtin("memo"),
2290
2291
  vec![
2291
- PyCallItem::Arg(
2292
- a.str(
2293
- format!("{}:{}:{}", ctx.filename, linecol.0, linecol.1)
2294
- )
2295
- ),
2292
+ PyCallItem::Arg(a.str(format!(
2293
+ "{}:{}:{}:{:08x}",
2294
+ ctx.filename,
2295
+ linecol.0,
2296
+ linecol.1,
2297
+ {
2298
+ let mut hasher = std::collections::hash_map::DefaultHasher::new();
2299
+ ctx.source[span.start..span.end].hash(&mut hasher);
2300
+ hasher.finish() as u32
2301
+ }
2302
+ ))),
2296
2303
  PyCallItem::Arg(
2297
2304
  a.tuple(
2298
2305
  nonlocals
@@ -2304,7 +2311,14 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for SExpr<'src> {
2304
2311
  ),
2305
2312
  PyCallItem::Arg(callback),
2306
2313
  ],
2307
- ))
2314
+ );
2315
+
2316
+ if memo_captures.is_do {
2317
+ // TODO should we really automatically bind a monadic expression for convenience?
2318
+ a.yield_(a.yield_(memo_call))
2319
+ } else {
2320
+ a.yield_(memo_call)
2321
+ }
2308
2322
  }
2309
2323
  Expr::Await(expr) => a.await_(pre.bind(expr.transform(ctx)?)),
2310
2324
  Expr::Yield(expr) => a.yield_(pre.bind(expr.transform(ctx)?)),
@@ -3,9 +3,17 @@ export import .result.*
3
3
  export import .async.*
4
4
  export import .reader.*
5
5
  export import .memo.*
6
+ export import .list.*
6
7
 
7
- export Fn = class:
8
- compose = staticmethod& (*args) =>
8
+ named = name => fn =>
9
+ fn.__name__ = name
10
+ fn.__qualname__ = name
11
+ fn
12
+
13
+ methods = {
14
+ id: named("id")& x => x
15
+
16
+ compose: named("compose")& (*args) =>
9
17
  args match:
10
18
  [] => raise ValueError("At least one function is required for composition")
11
19
  [f] => f
@@ -21,4 +29,9 @@ export Fn = class:
21
29
  composed.signature = fs[-1].signature
22
30
 
23
31
  composed
24
- default raise ValueError("Invalid arguments for Fn.compose()")
32
+ default raise ValueError("Invalid arguments for Fn.compose()")
33
+ }
34
+
35
+ globals().update(methods)
36
+
37
+ __all__ = methods.keys() | tuple
@@ -0,0 +1,26 @@
1
+ import collections.UserList
2
+ import itertools
3
+
4
+ import .monad.Monad
5
+ import ..iterable.Iterable
6
+
7
+ export List = class(UserList, Monad):
8
+ bind_once = (self, f) =>
9
+ raise NotImplementedError(
10
+ "Binding List in a do-block doesn't work in Koatl " +
11
+ "due to generator limitations. " +
12
+ "Wrap in Ok to use the regular Result monad."
13
+ )
14
+
15
+ bind = (self, f) => List(self.flat_map(f))
16
+
17
+ pure = staticmethod& x => [x]
18
+
19
+ traverse = Iterable.traverse
20
+
21
+
22
+ for name, method in List.__dict__:
23
+ if name.startswith("_"):
24
+ continue
25
+
26
+ register_global_attr(list, name, method)
@@ -1,12 +1,18 @@
1
1
  import abc
2
2
 
3
3
  export Monad = class(abc.ABC):
4
+ # The default implementation required for `@` syntax that should be overridden by subclasses.
5
+ bind_once = abc.abstractmethod& (self, f) => None
6
+
7
+ pure = staticmethod& abc.abstractmethod& value => None
8
+
9
+ # Automatically given by bind_once.
4
10
  bind = (self, f) => self.bind_once(f)
5
11
 
6
12
  # An optional, optimized implementation of `bind` that skips deep recursion.
13
+ # TODO: can this be automatically generated?
7
14
  bind_gen = (self, gen) => raise NotImplementedError()
8
15
 
9
- # The default implementation required for `@` syntax that should be overridden by subclasses.
10
- bind_once = abc.abstractmethod& (self, f) => None
11
-
12
- pure = staticmethod& abc.abstractmethod& value => None
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)))