1- """Implementation of IO Actions.
1+ """Implementation of IO Actions."""
22
3- Many thanks to Chris Taylor and his excellent blog post "IO Is Pure",
4- http://chris-taylor.github.io/blog/2013/02/09/io-is-not-a-side-effect/
5- """
6-
7- from typing import Any , Callable , Generic , TypeVar , Tuple , Optional
3+ from abc import abstractmethod
4+ from typing import Any , Callable , Generic , TypeVar , Tuple
85
96from .typing import Applicative
107from .typing import Functor
118from .typing import Monad
12- from .util import indent as ind
9+ from .util import indent as ind , Unit
1310
1411TSource = TypeVar ("TSource" )
1512TResult = TypeVar ("TResult" )
@@ -24,40 +21,24 @@ class IO(Generic[TSource]):
2421 happen.
2522 """
2623
27- def __init__ (self , value : Optional [TSource ] = None ) -> None :
28- """Create IO Action."""
29-
30- super ().__init__ ()
31- self ._value = value
32-
3324 @classmethod
3425 def unit (cls , value : TSource ):
35- return cls (value )
26+ return Return (value )
3627
37- def bind (self , func : Callable [[Optional [TSource ]], "IO[Optional[TResult]]" ]) -> "IO[Optional[TResult]]" :
28+ @abstractmethod
29+ def bind (self , func : Callable [[TSource ], "IO[TResult]" ]) -> "IO[TResult]" :
3830 """IO a -> (a -> IO b) -> IO b."""
3931
40- return func (self ._value )
41-
42- @classmethod
43- def pure (
44- cls , value : Optional [Callable [[Optional [TSource ]], Optional [TResult ]]]
45- ) -> "IO[Optional[Callable[[Optional[TSource]], Optional[TResult]]]]" :
46- return IO (value )
47-
48- def apply (
49- self : "IO[Optional[Callable[[Optional[TSource]], Optional[TResult]]]]" , something : "IO[Optional[TSource]]"
50- ) -> "IO[Optional[TResult]]" :
51- """Apply wrapped function over something."""
52- assert self ._value is not None
53- return something .map (self ._value )
32+ raise NotImplementedError
5433
55- def map (self , func : Callable [[Optional [TSource ]], Optional [TResult ]]) -> "IO[Optional[TResult]]" :
56- return IO (func (self ._value ))
34+ @abstractmethod
35+ def map (self , func : Callable [[TSource ], TResult ]) -> "IO[TResult]" :
36+ raise NotImplementedError
5737
58- def run (self , world : int ) -> Optional [TSource ]:
38+ @abstractmethod
39+ def run (self , world : int ) -> TSource :
5940 """Run IO action."""
60- return self . _value
41+ raise NotImplementedError
6142
6243 def __or__ (self , func ):
6344 """Use | as operator for bind.
@@ -67,49 +48,71 @@ def __or__(self, func):
6748 return self .bind (func )
6849
6950 def __call__ (self , world : int = 0 ) -> Any :
70- """Nothing more to run ."""
51+ """Run io action ."""
7152 return self .run (world )
7253
54+ @abstractmethod
7355 def __str__ (self , m : int = 0 , n : int = 0 ) -> str :
74- a = self ._value
75- return "%sReturn %s" % (ind (m ), [a ])
56+ raise NotImplementedError
7657
7758 def __repr__ (self ) -> str :
7859 return self .__str__ ()
7960
8061
81- class Put (IO ):
62+ class Return (IO [TSource ]):
63+ def __init__ (self , value : TSource ) -> None :
64+ """Create IO Action."""
65+
66+ self ._value = value
67+
68+ def map (self , func : Callable [[TSource ], TResult ]) -> "IO[TResult]" :
69+ return Return (func (self ._value ))
70+
71+ def bind (self , func : Callable [[TSource ], "IO[TResult]" ]) -> "IO[TResult]" :
72+ """IO a -> (a -> IO b) -> IO b."""
73+
74+ return func (self ._value )
75+
76+ def run (self , world : int ) -> TSource :
77+ """Run IO action."""
78+ return self ._value
79+
80+ def __str__ (self , m : int = 0 , n : int = 0 ) -> str :
81+ a = self ._value
82+ return f"{ ind (m )} Return { a } "
83+
84+
85+ class Put (IO [TSource ]):
8286 """The Put action.
8387
8488 A container holding a string to be printed to stdout, followed by
8589 another IO Action.
8690 """
8791
88- def __init__ (self , text : str , action : IO ) -> None :
89- super (). __init__ (( text , action ))
92+ def __init__ (self , text : str , io : IO ) -> None :
93+ self . _value = text , io
9094
91- def bind (self , func : Callable [[Optional [ TSource ]] , IO [Optional [ TResult ]]] ) -> "Put" :
95+ def bind (self , func : Callable [[TSource ], IO [TResult ]]) -> 'IO[TResult]' :
9296 """IO a -> (a -> IO b) -> IO b"""
9397
94- assert self ._value is not None
95- text , a = self ._value
96- return Put (text , a .bind (func ))
98+ text , io = self ._value
99+ return Put (text , io .bind (func ))
97100
98- def map (self , func : Callable [[Optional [ TSource ]], Optional [ TResult ]] ) -> "Put " :
101+ def map (self , func : Callable [[TSource ], TResult ]) -> "IO[TResult] " :
99102 # Put s (fmap f io)
100103 assert self ._value is not None
101104 text , action = self ._value
102105 return Put (text , action .map (func ))
103106
104- def run (self , world : int ) -> IO :
107+ def run (self , world : int ) -> TSource :
105108 """Run IO action"""
106109
107110 assert self ._value is not None
108111 text , action = self ._value
109112 new_world = pure_print (world , text )
110113 return action (world = new_world )
111114
112- def __call__ (self , world : int = 0 ) -> IO :
115+ def __call__ (self , world : int = 0 ) -> TSource :
113116 return self .run (world )
114117
115118 def __str__ (self , m : int = 0 , n : int = 0 ) -> str :
@@ -119,96 +122,91 @@ def __str__(self, m: int = 0, n: int = 0) -> str:
119122 return '%sPut ("%s",\n %s\n %s)' % (ind (m ), s , a , ind (m ))
120123
121124
122- class Get (IO ):
123- """A container holding a function from string -> IO, which can
125+ class Get (IO [ TSource ] ):
126+ """A container holding a function from string -> IO[TSource] , which can
124127 be applied to whatever string is read from stdin.
125128 """
126129
127- def __init__ (self , func : Callable [[str ], IO ]) -> None :
128- super (). __init__ ( func )
130+ def __init__ (self , fn : Callable [[str ], IO [ TSource ] ]) -> None :
131+ self . _fn = fn
129132
130- def bind (self , func : Callable [[Any ], IO ] ) -> IO :
133+ def bind (self , func : Callable [[TSource ], IO [ TResult ]] ) -> IO [ TResult ] :
131134 """IO a -> (a -> IO b) -> IO b"""
132135
133- assert self ._value is not None
134- g = self ._value
136+ g = self ._fn
135137 return Get (lambda text : g (text ).bind (func ))
136138
137- def map (self , func : Callable [[Any ], Any ]) -> "Get" :
139+ def map (self , func : Callable [[TSource ], TResult ]) -> IO [ TResult ] :
138140 # Get (\s -> fmap f (g s))
139- assert self ._value is not None
140- g = self ._value
141+ g = self ._fn
141142 return Get (lambda s : g (s ).map (func ))
142143
143- def run (self , world : int ) -> IO :
144+ def run (self , world : int ) -> TSource :
144145 """Run IO Action"""
145146
146- assert self ._value is not None
147- func = self ._value
147+ func = self ._fn
148148 new_world , text = pure_input (world )
149149 action = func (text )
150150 return action (world = new_world )
151151
152- def __call__ (self , world : int = 0 ) -> IO :
152+ def __call__ (self , world : int = 0 ) -> TSource :
153153 return self .run (world )
154154
155155 def __str__ (self , m : int = 0 , n : int = 0 ) -> str :
156- assert self ._value is not None
157- g = self ._value
156+ g = self ._fn
158157 i = "x%s" % n
159158 a = g (i ).__str__ (m + 1 , n + 1 )
160159 return "%sGet (%s => \n %s\n %s)" % (ind (m ), i , a , ind (m ))
161160
162161
163- class ReadFile (IO ):
164- """A container holding a filename and a function from string -> IO,
162+ class ReadFile (IO [ str ] ):
163+ """A container holding a filename and a function from string -> IO[str] ,
165164 which can be applied to whatever string is read from the file.
166165 """
167166
168167 def __init__ (self , filename : str , func : Callable [[str ], IO ]) -> None :
169- super ().__init__ ((filename , func ))
170168 self .open_func = open
171- self ._get_value = lambda : ( filename , func )
169+ self ._value = filename , func
172170
173171 def bind (self , func : Callable [[Any ], IO ]) -> IO :
174172 """IO a -> (a -> IO b) -> IO b"""
175173
176- filename , g = self ._get_value ()
174+ filename , g = self ._value
177175 return ReadFile (filename , lambda s : g (s ).bind (func ))
178176
179177 def map (self , func : Callable [[Any ], Any ]) -> IO :
180178 # Get (\s -> fmap f (g s))
181- filename , g = self ._get_value ()
179+ filename , g = self ._value
182180 return Get (lambda s : g (s ).map (func ))
183181
184- def run (self , world : int ) -> IO :
182+ def run (self , world : int ) -> str :
185183 """Run IO Action"""
186184
187- filename , func = self ._get_value ()
185+ filename , func = self ._value
188186 f = self .open_func (filename )
189187 action = func (f .read ())
190188 return action (world = world + 1 )
191189
192- def __call__ (self , world : int = 0 ) -> IO :
190+ def __call__ (self , world : int = 0 ) -> str :
193191 return self .run (world )
194192
195193 def __str__ (self , m : int = 0 , n : int = 0 ) -> str :
196- filename , g = self ._get_value ()
194+ filename , g = self ._value
197195 i = "x%s" % n
198196 a = g (i ).__str__ (m + 2 , n + 1 )
199197 return '%sReadFile ("%s",%s => \n %s\n %s)' % (ind (m ), filename , i , a , ind (m ))
200198
201199
202- def get_line () -> IO :
203- return Get (lambda text : IO ( text ) )
200+ def get_line () -> IO [ str ] :
201+ return Get (Return )
204202
205203
206- def put_line (string : str ) -> IO :
207- return Put (string , IO ( None ))
204+ def put_line (text : str ) -> IO :
205+ return Put (text , Return ( Unit ))
208206
209207
210208def read_file (filename : str ) -> IO :
211- return ReadFile (filename , lambda text : IO ( text ) )
209+ return ReadFile (filename , Return )
212210
213211
214212def pure_print (world : int , text : str ) -> int :
@@ -222,17 +220,13 @@ def pure_input(world: int) -> Tuple[int, str]:
222220
223221
224222assert isinstance (IO , Functor )
225- assert isinstance (IO , Applicative )
226223assert isinstance (IO , Monad )
227224
228225assert isinstance (Put , Functor )
229- assert isinstance (Put , Applicative )
230226assert isinstance (Put , Monad )
231227
232228assert isinstance (Get , Functor )
233- assert isinstance (Get , Applicative )
234229assert isinstance (Get , Monad )
235230
236231assert isinstance (ReadFile , Functor )
237- assert isinstance (ReadFile , Applicative )
238232assert isinstance (ReadFile , Monad )
0 commit comments