-
Notifications
You must be signed in to change notification settings - Fork 0
/
mapped.py
92 lines (65 loc) · 2.51 KB
/
mapped.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
"""
Type transformation on Variadic Generics
"""
from __future__ import annotations
from typing import Callable, cast, reveal_type
type Identity[X] = X
class Mapped[F, *Ts](tuple):
"""
Holds elements of type F[T] such that T belongs to set of types Ts.
>>> Mapped[F, *Ts] = {F[T]; ∀ T ∈ Ts}
"""
def flatmap[F2](self, f2: Callable[[F], F2]) -> Mapped[F2, *Ts]:
return Functor(f2)(*self) # type: ignore
@staticmethod
def identity[*Is](*args: *Is) -> Mapped[Identity, *Is]:
return Mapped[Identity, *Is](args)
class Functor[F, T]:
"""
Functor applies a map/transformation F on elements of type T
such that T belongs to set of types Ts.
The set of mapped/transformed elements is Mapped[F, *Ts].
"""
def __init__(self, map: Callable[[T], F]):
self.map = map
def __call__[*Ts](self, *ts: *Ts) -> Mapped[F, *Ts]:
return Mapped[F, *Ts](tuple([self.map(cast(T, t)) for t in ts]))
class FlatMap[FIn, FOut]:
"""
Applies a transformation FOut on transformed elements Mapped[FIn, Ts].
The result is Mapped[FOut, *Ts].
>>> Mapped[FOut, *Ts] = {f_out(f_in_t); ∀ f_in_t ∈ Mapped[FIn, *Ts]}
>>> f_in_t is of type FIn[T]
>>> f_out(f_in_t) is of type FOut[T]
"""
def __init__(self, f_out: Callable[[FIn], FOut]) -> None:
self.functor = Functor[FOut, FIn](f_out)
def __call__[*Ts](self, t: Mapped[FIn, *Ts]) -> Mapped[FOut, *Ts]:
return self.functor(*t) # type: ignore
t0 = Mapped.identity(1, "1")
reveal_type(t0) # Pylance: Mapped[Identity[Unknown], int, str]
# Pycharm: Mapped[X, int, str]
# mypy: Any
print(t0) # (1, '1')
def f1(x) -> list:
return [x]
# t1 = Functor(lambda x: [x])(1, "1")
# t1 = t0.flatmap(lambda x: [x])
t1 = t0.flatmap(f1)
reveal_type(t1) # Pylance: Mapped[list[Unknown], int, str]
# Pycharm: Mapped[list, int, str]
# mypy: Any
print(t1) # ([1], ['1'])
def f2(x: list) -> type:
return type(x[0])
# t2 = t1.flatmap(lambda x: type(x[0]))
t2 = t1.flatmap(f2)
reveal_type(t2) # Pylance: Mapped[type[Unknown], int, str]
# Pycharm: Mapped[type, int, str]
# mypy: Any
print(t2) # (<class 'int'>, <class 'str'>)
t2_again = FlatMap(f2)(t1)
reveal_type(t2_again) # Pylance: Mapped[type, int, str]
# Pycharm: Any
# mypy: Mapped[builtins.type, Unpack[builtins.tuple[Any, ...]]]
print(t2_again) # (<class 'int'>, <class 'str'>)