morphir-python 0.0.1__py3-none-any.whl
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.
- morphir/__init__.py +14 -0
- morphir/ir/__init__.py +110 -0
- morphir/ir/access_controlled.py +15 -0
- morphir/ir/decorations.py +19 -0
- morphir/ir/diagnostics.py +105 -0
- morphir/ir/distribution.py +21 -0
- morphir/ir/document.py +37 -0
- morphir/ir/documented.py +11 -0
- morphir/ir/format_version.py +483 -0
- morphir/ir/fqname.py +9 -0
- morphir/ir/json.py +11 -0
- morphir/ir/literal.py +25 -0
- morphir/ir/meta.py +11 -0
- morphir/ir/migrate.py +1267 -0
- morphir/ir/module.py +17 -0
- morphir/ir/name.py +27 -0
- morphir/ir/package.py +15 -0
- morphir/ir/path.py +25 -0
- morphir/ir/qname.py +9 -0
- morphir/ir/ref.py +17 -0
- morphir/ir/type.py +37 -0
- morphir/ir/type_constraints.py +33 -0
- morphir/ir/type_def.py +19 -0
- morphir/ir/type_spec.py +25 -0
- morphir/ir/v3/__init__.py +64 -0
- morphir/ir/v3/_legacy_value.py +201 -0
- morphir/ir/v3/access_controlled.py +31 -0
- morphir/ir/v3/codec.py +1275 -0
- morphir/ir/v3/codec_v1.py +491 -0
- morphir/ir/v3/codec_v2.py +187 -0
- morphir/ir/v3/distribution.py +42 -0
- morphir/ir/v3/documented.py +13 -0
- morphir/ir/v3/fqname.py +30 -0
- morphir/ir/v3/literal.py +46 -0
- morphir/ir/v3/module.py +32 -0
- morphir/ir/v3/name.py +42 -0
- morphir/ir/v3/package.py +23 -0
- morphir/ir/v3/path.py +29 -0
- morphir/ir/v3/qname.py +16 -0
- morphir/ir/v3/type.py +126 -0
- morphir/ir/v3/value.py +235 -0
- morphir/ir/v4/__init__.py +160 -0
- morphir/ir/v4/access_controlled.py +17 -0
- morphir/ir/v4/annotation.py +75 -0
- morphir/ir/v4/codec.py +2161 -0
- morphir/ir/v4/decorations.py +88 -0
- morphir/ir/v4/distribution.py +56 -0
- morphir/ir/v4/document.py +69 -0
- morphir/ir/v4/documented.py +10 -0
- morphir/ir/v4/fqname.py +69 -0
- morphir/ir/v4/incompleteness.py +78 -0
- morphir/ir/v4/json.py +103 -0
- morphir/ir/v4/literal.py +51 -0
- morphir/ir/v4/meta.py +27 -0
- morphir/ir/v4/module.py +30 -0
- morphir/ir/v4/name.py +69 -0
- morphir/ir/v4/package.py +19 -0
- morphir/ir/v4/path.py +50 -0
- morphir/ir/v4/qname.py +34 -0
- morphir/ir/v4/ref.py +35 -0
- morphir/ir/v4/type.py +140 -0
- morphir/ir/v4/type_constraints.py +91 -0
- morphir/ir/v4/type_def.py +38 -0
- morphir/ir/v4/type_spec.py +64 -0
- morphir/ir/v4/value.py +480 -0
- morphir/ir/value.py +81 -0
- morphir/py.typed +0 -0
- morphir/sdk/__init__.py +110 -0
- morphir/sdk/_compare.py +187 -0
- morphir/sdk/aggregate.py +421 -0
- morphir/sdk/basics.py +504 -0
- morphir/sdk/char.py +110 -0
- morphir/sdk/decimal.py +311 -0
- morphir/sdk/dict.py +336 -0
- morphir/sdk/instant.py +118 -0
- morphir/sdk/int.py +86 -0
- morphir/sdk/key.py +522 -0
- morphir/sdk/list.py +356 -0
- morphir/sdk/local_date.py +436 -0
- morphir/sdk/local_time.py +267 -0
- morphir/sdk/maybe.py +135 -0
- morphir/sdk/number.py +237 -0
- morphir/sdk/regex.py +287 -0
- morphir/sdk/result.py +162 -0
- morphir/sdk/result_list.py +169 -0
- morphir/sdk/rule.py +103 -0
- morphir/sdk/set.py +214 -0
- morphir/sdk/stateful_app.py +49 -0
- morphir/sdk/string.py +427 -0
- morphir/sdk/tuple.py +43 -0
- morphir/sdk/uuid.py +209 -0
- morphir_python-0.0.1.dist-info/METADATA +269 -0
- morphir_python-0.0.1.dist-info/RECORD +99 -0
- morphir_python-0.0.1.dist-info/WHEEL +4 -0
- morphir_python-0.0.1.dist-info/entry_points.txt +2 -0
- morphir_python-0.0.1.dist-info/licenses/LICENSE +201 -0
- morphir_tools/__init__.py +13 -0
- morphir_tools/cli.py +223 -0
- morphir_tools/py.typed +0 -0
morphir/sdk/list.py
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""Morphir.SDK.List: elm/core's List module plus the Morphir joins.
|
|
2
|
+
|
|
3
|
+
An Elm `List a` is an immutable Python `tuple[A, ...]`. Functions accept any
|
|
4
|
+
`Sequence` as input and always yield a tuple; no function mutates its input.
|
|
5
|
+
All functions take their arguments in Elm order, with the list last.
|
|
6
|
+
|
|
7
|
+
Many names in this module shadow Python built-ins (`all`, `any`, `filter`,
|
|
8
|
+
`map`, `range`, `sum`). Import the module with an alias, not the names:
|
|
9
|
+
|
|
10
|
+
>>> from morphir.sdk import list as List
|
|
11
|
+
>>> List.map(lambda n: n * 2, (1, 2, 3))
|
|
12
|
+
(2, 4, 6)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import builtins
|
|
16
|
+
import math
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
18
|
+
|
|
19
|
+
from morphir.sdk import _compare
|
|
20
|
+
from morphir.sdk._compare import Comparable, Order
|
|
21
|
+
from morphir.sdk.maybe import Just, Maybe, Nothing
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from collections.abc import Callable, Sequence
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"List",
|
|
28
|
+
"all",
|
|
29
|
+
"any",
|
|
30
|
+
"append",
|
|
31
|
+
"concat",
|
|
32
|
+
"concat_map",
|
|
33
|
+
"cons",
|
|
34
|
+
"drop",
|
|
35
|
+
"filter",
|
|
36
|
+
"filter_map",
|
|
37
|
+
"foldl",
|
|
38
|
+
"foldr",
|
|
39
|
+
"head",
|
|
40
|
+
"indexed_map",
|
|
41
|
+
"inner_join",
|
|
42
|
+
"intersperse",
|
|
43
|
+
"is_empty",
|
|
44
|
+
"left_join",
|
|
45
|
+
"length",
|
|
46
|
+
"map",
|
|
47
|
+
"map2",
|
|
48
|
+
"map3",
|
|
49
|
+
"map4",
|
|
50
|
+
"map5",
|
|
51
|
+
"maximum",
|
|
52
|
+
"member",
|
|
53
|
+
"minimum",
|
|
54
|
+
"partition",
|
|
55
|
+
"product",
|
|
56
|
+
"range",
|
|
57
|
+
"repeat",
|
|
58
|
+
"reverse",
|
|
59
|
+
"singleton",
|
|
60
|
+
"sort",
|
|
61
|
+
"sort_by",
|
|
62
|
+
"sort_with",
|
|
63
|
+
"sum",
|
|
64
|
+
"tail",
|
|
65
|
+
"take",
|
|
66
|
+
"unzip",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
type List[A] = tuple[A, ...]
|
|
70
|
+
"""An immutable list."""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# Create
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def singleton[A](a: A) -> List[A]:
|
|
77
|
+
"""Create a list with one element."""
|
|
78
|
+
return (a,)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def repeat[A](n: int, a: A) -> List[A]:
|
|
82
|
+
"""Create a list with `n` copies of a value. A count below 1 yields `()`."""
|
|
83
|
+
return (a,) * n if n > 0 else ()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def range(low: int, high: int) -> List[int]:
|
|
87
|
+
"""Create the list of integers from `low` to `high`, both included."""
|
|
88
|
+
return tuple(builtins.range(low, high + 1))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def cons[A](head: A, tail: Sequence[A]) -> List[A]:
|
|
92
|
+
"""Add an element to the front of a list (Elm `::`)."""
|
|
93
|
+
return (head, *tail)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# Transform
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def map[A, B](f: Callable[[A], B], xs: Sequence[A]) -> List[B]:
|
|
100
|
+
"""Apply a function to every element of a list."""
|
|
101
|
+
return tuple(f(x) for x in xs)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def indexed_map[A, B](f: Callable[[int, A], B], xs: Sequence[A]) -> List[B]:
|
|
105
|
+
"""Apply a function to every element and its index, which starts at 0."""
|
|
106
|
+
return tuple(f(index, x) for index, x in enumerate(xs))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def foldl[A, B](f: Callable[[A, B], B], initial: B, xs: Sequence[A]) -> B:
|
|
110
|
+
"""Reduce a list from the left. The function gets the element first."""
|
|
111
|
+
accumulator = initial
|
|
112
|
+
for x in xs:
|
|
113
|
+
accumulator = f(x, accumulator)
|
|
114
|
+
return accumulator
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def foldr[A, B](f: Callable[[A, B], B], initial: B, xs: Sequence[A]) -> B:
|
|
118
|
+
"""Reduce a list from the right. The function gets the element first."""
|
|
119
|
+
accumulator = initial
|
|
120
|
+
for x in reversed(xs):
|
|
121
|
+
accumulator = f(x, accumulator)
|
|
122
|
+
return accumulator
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def filter[A](predicate: Callable[[A], bool], xs: Sequence[A]) -> List[A]:
|
|
126
|
+
"""Keep the elements that pass the test."""
|
|
127
|
+
return tuple(x for x in xs if predicate(x))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def filter_map[A, B](f: Callable[[A], Maybe[B]], xs: Sequence[A]) -> List[B]:
|
|
131
|
+
"""Apply a function to every element and keep the `Just` values."""
|
|
132
|
+
return tuple(m.value for m in (f(x) for x in xs) if isinstance(m, Just))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# Utilities
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def length[A](xs: Sequence[A]) -> int:
|
|
139
|
+
"""Return the number of elements in a list."""
|
|
140
|
+
return len(xs)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def reverse[A](xs: Sequence[A]) -> List[A]:
|
|
144
|
+
"""Reverse a list."""
|
|
145
|
+
return tuple(reversed(xs))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def member[A](a: A, xs: Sequence[A]) -> bool:
|
|
149
|
+
"""Check that a list contains a value, by structural equality."""
|
|
150
|
+
return next((True for x in xs if _compare.equal(a, x)), False)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def all[A](predicate: Callable[[A], bool], xs: Sequence[A]) -> bool:
|
|
154
|
+
"""Check that every element passes the test."""
|
|
155
|
+
return next((False for x in xs if not predicate(x)), True)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def any[A](predicate: Callable[[A], bool], xs: Sequence[A]) -> bool:
|
|
159
|
+
"""Check that at least one element passes the test."""
|
|
160
|
+
return next((True for x in xs if predicate(x)), False)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _extreme[A: Comparable](wanted: Order, xs: Sequence[A]) -> Maybe[A]:
|
|
164
|
+
if not xs:
|
|
165
|
+
return Nothing()
|
|
166
|
+
best = xs[0]
|
|
167
|
+
for x in xs[1:]:
|
|
168
|
+
if _compare.compare(x, best) is wanted:
|
|
169
|
+
best = x
|
|
170
|
+
return Just(best)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def maximum[A: Comparable](xs: Sequence[A]) -> Maybe[A]:
|
|
174
|
+
"""Return the largest element, or `Nothing` for an empty list."""
|
|
175
|
+
return _extreme(Order.GT, xs)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def minimum[A: Comparable](xs: Sequence[A]) -> Maybe[A]:
|
|
179
|
+
"""Return the smallest element, or `Nothing` for an empty list."""
|
|
180
|
+
return _extreme(Order.LT, xs)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def sum[N: (int, float)](xs: Sequence[N]) -> N | int:
|
|
184
|
+
"""Return the sum of the elements. The sum of an empty list is 0."""
|
|
185
|
+
return builtins.sum(xs)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def product[N: (int, float)](xs: Sequence[N]) -> N | int:
|
|
189
|
+
"""Return the product of the elements. The product of an empty list is 1."""
|
|
190
|
+
return math.prod(xs)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# Combine
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def append[A](xs: Sequence[A], ys: Sequence[A]) -> List[A]:
|
|
197
|
+
"""Put two lists together."""
|
|
198
|
+
return (*xs, *ys)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def concat[A](lists: Sequence[Sequence[A]]) -> List[A]:
|
|
202
|
+
"""Concatenate many lists into one."""
|
|
203
|
+
return tuple(x for xs in lists for x in xs)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def concat_map[A, B](f: Callable[[A], Sequence[B]], xs: Sequence[A]) -> List[B]:
|
|
207
|
+
"""Apply a function to every element and concatenate the lists it yields."""
|
|
208
|
+
return tuple(y for x in xs for y in f(x))
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def intersperse[A](separator: A, xs: Sequence[A]) -> List[A]:
|
|
212
|
+
"""Put a value between all the elements of a list."""
|
|
213
|
+
result: list[A] = []
|
|
214
|
+
for x in xs:
|
|
215
|
+
if result:
|
|
216
|
+
result.append(separator)
|
|
217
|
+
result.append(x)
|
|
218
|
+
return tuple(result)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def map2[A, B, R](f: Callable[[A, B], R], xs: Sequence[A], ys: Sequence[B]) -> List[R]:
|
|
222
|
+
"""Combine two lists element by element; stop at the end of the shortest."""
|
|
223
|
+
return tuple(f(*args) for args in zip(xs, ys, strict=False))
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def map3[A, B, C, R](
|
|
227
|
+
f: Callable[[A, B, C], R], xs: Sequence[A], ys: Sequence[B], zs: Sequence[C]
|
|
228
|
+
) -> List[R]:
|
|
229
|
+
"""Combine three lists element by element; stop at the end of the shortest."""
|
|
230
|
+
return tuple(f(*args) for args in zip(xs, ys, zs, strict=False))
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def map4[A, B, C, D, R](
|
|
234
|
+
f: Callable[[A, B, C, D], R],
|
|
235
|
+
ws: Sequence[A],
|
|
236
|
+
xs: Sequence[B],
|
|
237
|
+
ys: Sequence[C],
|
|
238
|
+
zs: Sequence[D],
|
|
239
|
+
) -> List[R]:
|
|
240
|
+
"""Combine four lists element by element; stop at the end of the shortest."""
|
|
241
|
+
return tuple(f(*args) for args in zip(ws, xs, ys, zs, strict=False))
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def map5[A, B, C, D, E, R](
|
|
245
|
+
f: Callable[[A, B, C, D, E], R],
|
|
246
|
+
vs: Sequence[A],
|
|
247
|
+
ws: Sequence[B],
|
|
248
|
+
xs: Sequence[C],
|
|
249
|
+
ys: Sequence[D],
|
|
250
|
+
zs: Sequence[E],
|
|
251
|
+
) -> List[R]:
|
|
252
|
+
"""Combine five lists element by element; stop at the end of the shortest."""
|
|
253
|
+
return tuple(f(*args) for args in zip(vs, ws, xs, ys, zs, strict=False))
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
# Sort
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def sort[A: Comparable](xs: Sequence[A]) -> List[A]:
|
|
260
|
+
"""Sort a list of comparable values from lowest to highest.
|
|
261
|
+
|
|
262
|
+
Raises:
|
|
263
|
+
TypeError: If the elements are not comparable with each other.
|
|
264
|
+
"""
|
|
265
|
+
return tuple(sorted(xs, key=_compare.sort_key(_compare.compare)))
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def sort_by[A, K: Comparable](f: Callable[[A], K], xs: Sequence[A]) -> List[A]:
|
|
269
|
+
"""Sort a list by a derived comparable key. The sort is stable.
|
|
270
|
+
|
|
271
|
+
Raises:
|
|
272
|
+
TypeError: If the keys are not comparable with each other.
|
|
273
|
+
"""
|
|
274
|
+
key = _compare.sort_key(_compare.compare)
|
|
275
|
+
return tuple(sorted(xs, key=lambda x: key(f(x))))
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def sort_with[A](f: Callable[[A, A], Order], xs: Sequence[A]) -> List[A]:
|
|
279
|
+
"""Sort a list with a custom comparison function. The sort is stable."""
|
|
280
|
+
return tuple(sorted(xs, key=_compare.sort_key(f)))
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
# Deconstruct
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def is_empty[A](xs: Sequence[A]) -> bool:
|
|
287
|
+
"""Check that a list is empty."""
|
|
288
|
+
return len(xs) == 0
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def head[A](xs: Sequence[A]) -> Maybe[A]:
|
|
292
|
+
"""Return the first element, or `Nothing` for an empty list."""
|
|
293
|
+
return Just(xs[0]) if xs else Nothing()
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def tail[A](xs: Sequence[A]) -> Maybe[List[A]]:
|
|
297
|
+
"""Return all elements but the first, or `Nothing` for an empty list."""
|
|
298
|
+
return Just(tuple(xs[1:])) if xs else Nothing()
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def take[A](n: int, xs: Sequence[A]) -> List[A]:
|
|
302
|
+
"""Take the first `n` elements of a list."""
|
|
303
|
+
return tuple(xs[:n]) if n > 0 else ()
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def drop[A](n: int, xs: Sequence[A]) -> List[A]:
|
|
307
|
+
"""Drop the first `n` elements of a list."""
|
|
308
|
+
return tuple(xs[n:]) if n > 0 else tuple(xs)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def partition[A](
|
|
312
|
+
predicate: Callable[[A], bool], xs: Sequence[A]
|
|
313
|
+
) -> tuple[List[A], List[A]]:
|
|
314
|
+
"""Split a list into the elements that pass the test and those that fail."""
|
|
315
|
+
passed: list[A] = []
|
|
316
|
+
failed: list[A] = []
|
|
317
|
+
for x in xs:
|
|
318
|
+
(passed if predicate(x) else failed).append(x)
|
|
319
|
+
return (tuple(passed), tuple(failed))
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def unzip[A, B](pairs: Sequence[tuple[A, B]]) -> tuple[List[A], List[B]]:
|
|
323
|
+
"""Split a list of pairs into a pair of lists."""
|
|
324
|
+
return (tuple(a for a, _ in pairs), tuple(b for _, b in pairs))
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
# Morphir additions to elm/core
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def inner_join[A, B](
|
|
331
|
+
list_b: Sequence[B], on: Callable[[A, B], bool], list_a: Sequence[A]
|
|
332
|
+
) -> List[tuple[A, B]]:
|
|
333
|
+
"""Join two lists like an SQL inner join.
|
|
334
|
+
|
|
335
|
+
The result keeps the order of `list_a` and has one pair for each element of
|
|
336
|
+
`list_b` that matches.
|
|
337
|
+
"""
|
|
338
|
+
return tuple((a, b) for a in list_a for b in list_b if on(a, b))
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def left_join[A, B](
|
|
342
|
+
list_b: Sequence[B], on: Callable[[A, B], bool], list_a: Sequence[A]
|
|
343
|
+
) -> List[tuple[A, Maybe[B]]]:
|
|
344
|
+
"""Join two lists like an SQL left outer join.
|
|
345
|
+
|
|
346
|
+
The result keeps the order of `list_a`. An element with no match in `list_b`
|
|
347
|
+
yields one pair with `Nothing`.
|
|
348
|
+
"""
|
|
349
|
+
result: list[tuple[A, Maybe[B]]] = []
|
|
350
|
+
for a in list_a:
|
|
351
|
+
matches = [b for b in list_b if on(a, b)]
|
|
352
|
+
if matches:
|
|
353
|
+
result.extend((a, Just(b)) for b in matches)
|
|
354
|
+
else:
|
|
355
|
+
result.append((a, Nothing()))
|
|
356
|
+
return tuple(result)
|