-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathval.py
136 lines (103 loc) · 2.96 KB
/
val.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
"""
Local Variables to work around Python annoying limitations about lambdas.
Python forbids local variables and
"""
from __future__ import annotations
from collections import abc
from dataclasses import dataclass
from typing import Generic, TypeVar, Callable, List, Any, final
from raffiot import io, resource
from raffiot.io import IO
from raffiot.resource import Resource
__all__ = [
"Val",
]
A = TypeVar("A")
B = TypeVar("B")
@final
@dataclass
class Val(Generic[A]):
"""
Immutable Value.
Used to create local "variables" in lambdas.
"""
__slots__ = "value"
value: A
def get(self) -> A:
"""
Get this Val value.
:return:
"""
return self.value
def get_io(self) -> IO[None, None, A]:
"""
Get this Val value.
:return:
"""
return io.defer(self.get)
def get_rs(self) -> Resource[None, None, A]:
"""
Get this Val value.
:return:
"""
return resource.defer(self.get)
@classmethod
def pure(cls, a: A) -> Val[A]:
"""
Create a new Val with value `a`
:param a: the value of this val.
:return:
"""
return Val(a)
def map(self, f: Callable[[A], B]) -> Val[B]:
"""
Create a new Val from this one by applying this **pure** function.
:param f:
:return:
"""
return Val(f(self.value))
def traverse(self, f: Callable[[A], IO[Any, Any, B]]) -> IO[Any, Any, Val[B]]:
"""
Create a new Val from this one by applying this `IO` function.
:param f:
:return:
"""
return io.defer_io(f, self.value).map(Val)
def flat_map(self, f: Callable[[A], Val[B]]) -> Val[B]:
"""
Create a new Val from this one.
:param f:
:return:
"""
return f(self.value)
def flatten(self: Val[Val[B]]) -> Val[B]: # A = Val[B]
""" "
Flatten this `Val[Val[A]]` into a `Val[A]`
"""
return Val(self.value.value)
@classmethod
def zip(cls, *vals: Val[A]) -> Val[List[A]]:
""" "
Group these list of Val into a Val of List
"""
if len(vals) == 1 and isinstance(vals[0], abc.Iterable):
return Val([x.value for x in vals[0]])
return Val([x.value for x in vals]) # type: ignore
def zip_with(self, *vals: Any) -> Val[List[A]]:
"""
Group this Val with other Val into a list of Val.
:param vals: other Val to combine with self.
:return:
"""
return Val.zip(self, *vals)
def ap(self, *arg: Val[A]) -> Val[B]:
"""
Apply the function contained in this Val to `args` Vals.
:param arg:
:return:
"""
if len(arg) == 1 and isinstance(arg[0], abc.Iterable):
l = [x.value for x in arg[0]]
else:
l = [x.value for x in arg]
return Val(self.value(*l)) # type: ignore