20
20
21
21
import builtins
22
22
import functools
23
- from typing import Any , Callable , Iterable , List , Optional , Tuple , TypeVar , cast , get_origin , overload
23
+ from typing import (
24
+ Any ,
25
+ Callable ,
26
+ Generic ,
27
+ Iterable ,
28
+ Iterator ,
29
+ List ,
30
+ Optional ,
31
+ Protocol ,
32
+ Tuple ,
33
+ TypeVar ,
34
+ cast ,
35
+ get_origin ,
36
+ overload ,
37
+ )
24
38
25
39
from expression .core import Case , Nothing , Option , Some , pipe
26
40
37
51
T6 = TypeVar ("T6" )
38
52
39
53
40
- class FrozenList (Tuple [TSource ]):
54
+ class FrozenList (Generic [TSource ]):
41
55
"""Immutable list type.
42
56
43
57
Is faster than `List` for prepending, but slower for
@@ -55,6 +69,11 @@ class FrozenList(Tuple[TSource]):
55
69
>>> ys = empty.cons(1).cons(2).cons(3).cons(4).cons(5)
56
70
"""
57
71
72
+ def __init__ (self , value : Optional [Iterable [TSource ]] = None ) -> None :
73
+ # Use composition instead of inheritance since generic tuples
74
+ # are not suppored by mypy.
75
+ self .value : Tuple [TSource , ...] = tuple (value ) if value else tuple ()
76
+
58
77
def match (self , pattern : Any ) -> Any :
59
78
case : Case [TSource ] = Case (self )
60
79
return case (pattern ) if pattern else case
@@ -113,7 +132,7 @@ def pipe(self, *args: Any) -> Any:
113
132
def append (self , other : "FrozenList[TSource]" ) -> "FrozenList[TSource]" :
114
133
"""Append frozen list to end of the frozen list."""
115
134
116
- return FrozenList (self + other )
135
+ return FrozenList (self . value + other . value )
117
136
118
137
def choose (self , chooser : Callable [[TSource ], Option [TResult ]]) -> "FrozenList[TResult]" :
119
138
"""Choose items from the list.
@@ -136,14 +155,14 @@ def mapper(x: TSource) -> FrozenList[TResult]:
136
155
return self .collect (mapper )
137
156
138
157
def collect (self , mapping : Callable [[TSource ], "FrozenList[TResult]" ]) -> "FrozenList[TResult]" :
139
- mapped = builtins .map (mapping , self )
158
+ mapped = builtins .map (mapping , self . value )
140
159
xs = (y for x in mapped for y in x )
141
160
return FrozenList (xs )
142
161
143
162
def cons (self , element : TSource ) -> "FrozenList[TSource]" :
144
163
"""Add element to front of List."""
145
164
146
- return FrozenList ((element ,) + self ) # NOTE: Faster than (element, *self)
165
+ return FrozenList ((element ,) + self . value ) # NOTE: Faster than (element, *self)
147
166
148
167
def filter (self , predicate : Callable [[TSource ], bool ]) -> "FrozenList[TSource]" :
149
168
"""Filter list.
@@ -158,7 +177,7 @@ def filter(self, predicate: Callable[[TSource], bool]) -> "FrozenList[TSource]":
158
177
A list containing only the elements that satisfy the
159
178
predicate.
160
179
"""
161
- return FrozenList (builtins .filter (predicate , self ))
180
+ return FrozenList (builtins .filter (predicate , self . value ))
162
181
163
182
def fold (self , folder : Callable [[TState , TSource ], TState ], state : TState ) -> TState :
164
183
"""Applies a function to each element of the collection,
@@ -263,7 +282,12 @@ def range(stop: int) -> "FrozenList[int]":
263
282
264
283
@overload
265
284
@staticmethod
266
- def range (start : int , stop : int , step : Optional [int ] = None ) -> "FrozenList[int]" :
285
+ def range (start : int , stop : int ) -> "FrozenList[int]" :
286
+ ...
287
+
288
+ @overload
289
+ @staticmethod
290
+ def range (start : int , stop : int , step : int ) -> "FrozenList[int]" :
267
291
...
268
292
269
293
@staticmethod
@@ -283,15 +307,15 @@ def skip(self, count: int) -> "FrozenList[TSource]":
283
307
Returns:
284
308
The list after removing the first N elements.
285
309
"""
286
- return FrozenList (self [count :])
310
+ return FrozenList (self . value [count :])
287
311
288
312
def skip_last (self , count : int ) -> "FrozenList[TSource]" :
289
- return FrozenList (self [:- count ])
313
+ return FrozenList (self . value [:- count ])
290
314
291
315
def tail (self ) -> "FrozenList[TSource]" :
292
316
"""Return tail of List."""
293
317
294
- _ , * tail = self
318
+ _ , * tail = self . value
295
319
return FrozenList (tail )
296
320
297
321
def take (self , count : int ) -> "FrozenList[TSource]" :
@@ -303,7 +327,7 @@ def take(self, count: int) -> "FrozenList[TSource]":
303
327
Returns:
304
328
The result list.
305
329
"""
306
- return FrozenList (self [:count ])
330
+ return FrozenList (self . value [:count ])
307
331
308
332
def take_last (self , count : int ) -> "FrozenList[TSource]" :
309
333
"""Returns a specified number of contiguous elements from the
@@ -315,13 +339,17 @@ def take_last(self, count: int) -> "FrozenList[TSource]":
315
339
Returns:
316
340
The result list.
317
341
"""
318
- return FrozenList (self [- count :])
342
+ return FrozenList (self . value [- count :])
319
343
320
344
def try_head (self ) -> Option [TSource ]:
321
345
"""Returns the first element of the list, or None if the list is
322
346
empty.
323
347
"""
324
- return Some (self [0 ]) if self else Nothing
348
+ if self .value :
349
+ value = cast ("TSource" , self .value [0 ])
350
+ return Some (value )
351
+
352
+ return Nothing
325
353
326
354
@staticmethod
327
355
def unfold (generator : Callable [[TState ], Option [Tuple [TSource , TState ]]], state : TState ) -> "FrozenList[TSource]" :
@@ -352,7 +380,30 @@ def zip(self, other: "FrozenList[TResult]") -> "FrozenList[Tuple[TSource, TResul
352
380
A single list containing pairs of matching elements from the
353
381
input lists.
354
382
"""
355
- return of_seq (builtins .zip (self , other ))
383
+ return of_seq (builtins .zip (self .value , other .value ))
384
+
385
+ def __add__ (self , other : "FrozenList[TSource]" ) -> "FrozenList[TSource]" :
386
+ return FrozenList (self .value + other .value )
387
+
388
+ @overload
389
+ def __getitem__ (self , key : slice ) -> "FrozenList[TSource]" :
390
+ ...
391
+
392
+ @overload
393
+ def __getitem__ (self , key : int ) -> TSource :
394
+ ...
395
+
396
+ def __getitem__ (self , key : Any ) -> Any :
397
+ return self .value [key ]
398
+
399
+ def __iter__ (self ) -> Iterator [TSource ]:
400
+ return iter (self .value )
401
+
402
+ def __eq__ (self , other : Any ) -> bool :
403
+ return self .value == other
404
+
405
+ def __len__ (self ) -> int :
406
+ return len (self .value )
356
407
357
408
def __match__ (self , pattern : Any ) -> Iterable [List [TSource ]]:
358
409
if self == pattern :
@@ -374,6 +425,20 @@ def __repr__(self) -> str:
374
425
return str (self )
375
426
376
427
428
+ class ExitFn (Protocol ):
429
+ """A partially applied exit function."""
430
+
431
+ def __call__ (self , source : FrozenList [TSource ]) -> TSource :
432
+ ...
433
+
434
+
435
+ class FilterFn (Protocol ):
436
+ """A partially applied filter function."""
437
+
438
+ def __call__ (self , source : FrozenList [TSource ]) -> FrozenList [TSource ]:
439
+ ...
440
+
441
+
377
442
def append (source : FrozenList [TSource ]) -> Callable [[FrozenList [TSource ]], FrozenList [TSource ]]:
378
443
def _append (other : FrozenList [TSource ]) -> FrozenList [TSource ]:
379
444
return source .append (other )
@@ -513,7 +578,7 @@ def indexed(source: FrozenList[TSource]) -> FrozenList[Tuple[int, TSource]]:
513
578
return source .indexed ()
514
579
515
580
516
- def item (index : int ) -> Callable [[ FrozenList [ TSource ]], TSource ] :
581
+ def item (index : int ) -> ExitFn :
517
582
"""Indexes into the list. The first element has index 0.
518
583
519
584
Args:
@@ -575,7 +640,12 @@ def range(stop: int) -> FrozenList[int]:
575
640
576
641
577
642
@overload
578
- def range (start : int , stop : int , step : Optional [int ] = None ) -> FrozenList [int ]:
643
+ def range (start : int , stop : int ) -> FrozenList [int ]:
644
+ ...
645
+
646
+
647
+ @overload
648
+ def range (start : int , stop : int , step : int ) -> FrozenList [int ]:
579
649
...
580
650
581
651
@@ -587,7 +657,7 @@ def singleton(value: TSource) -> FrozenList[TSource]:
587
657
return FrozenList ((value ,))
588
658
589
659
590
- def skip (count : int ) -> Callable [[ FrozenList [ TSource ]], FrozenList [ TSource ]] :
660
+ def skip (count : int ) -> FilterFn :
591
661
"""Returns the list after removing the first N elements.
592
662
593
663
Args:
@@ -603,7 +673,7 @@ def _skip(source: FrozenList[TSource]) -> FrozenList[TSource]:
603
673
return _skip
604
674
605
675
606
- def skip_last (count : int ) -> Callable [[ FrozenList [ TSource ]], FrozenList [ TSource ]] :
676
+ def skip_last (count : int ) -> FilterFn :
607
677
"""Returns the list after removing the last N elements.
608
678
609
679
Args:
@@ -623,7 +693,7 @@ def tail(source: FrozenList[TSource]) -> FrozenList[TSource]:
623
693
return source .tail ()
624
694
625
695
626
- def take (count : int ) -> Callable [[ FrozenList [ TSource ]], FrozenList [ TSource ]] :
696
+ def take (count : int ) -> FilterFn :
627
697
"""Returns the first N elements of the list.
628
698
629
699
Args:
@@ -639,7 +709,7 @@ def _take(source: FrozenList[TSource]) -> FrozenList[TSource]:
639
709
return _take
640
710
641
711
642
- def take_last (count : int ) -> Callable [[ FrozenList [ TSource ]], FrozenList [ TSource ]] :
712
+ def take_last (count : int ) -> FilterFn :
643
713
"""Returns a specified number of contiguous elements from the end of
644
714
the list.
645
715
0 commit comments