@@ -112,12 +112,12 @@ that implements the ``Mappable`` interface:
112
112
>> > maybe_str: Maybe[str ] = Some(' example' )
113
113
>> > assert maybe_str.map(can_be_mapped) == Some(' example!' )
114
114
115
- :class: `~returns.interfaces.mappable. MappableN ` interface help us to
115
+ :class: `~MappableN ` interface helps us to
116
116
create our own mappable container like :class: `~returns.maybe.Maybe `.
117
117
118
118
.. code :: python
119
119
120
- >> > from typing import Any, Callable, TypeVar
120
+ >> > from typing import Callable, TypeVar
121
121
122
122
>> > from returns.interfaces.mappable import Mappable1
123
123
>> > from returns.primitives.hkt import SupportsKind1
@@ -154,12 +154,12 @@ With our ``Number`` mappable class we can compose easily math functions with it.
154
154
Laws
155
155
~~~~
156
156
157
- To make sure your mappable implementation is right, you can apply the
158
- Mappable laws on it to test.
157
+ To make sure your `` Mappable `` implementation is right,
158
+ you can apply the `` Mappable `` laws on it to test.
159
159
160
160
1. :func: `Identity Law <_LawSpec.identity_law> `:
161
- When we pass the identity function to the map method,
162
- the mappable has to be the same, unaltered .
161
+ When we pass the identity function to the `` map `` method,
162
+ the `` Mappable `` instance has to be the same, unchanged .
163
163
164
164
.. code :: python
165
165
@@ -201,44 +201,282 @@ Bindable
201
201
202
202
Bindable is something that we can bind with a function. Like
203
203
:class: `~returns.maybe.Maybe `, so
204
- :class: `~returns.interfaces.bindable. BindableN ` interface will help
204
+ :class: `~BindableN ` interface will help
205
205
us to create our custom bindable.
206
206
207
207
.. code :: python
208
208
209
- >> > from dataclasses import dataclass
210
- >> > from typing import Any, Callable, TypeVar
209
+ >> > from typing import Callable, TypeVar
211
210
212
211
>> > from returns.interfaces.bindable import Bindable1
213
- >> > from returns.primitives.hkt import SupportsKind1
212
+ >> > from returns.primitives.hkt import SupportsKind1, Kind1, dekind
214
213
>> > from returns.primitives.container import BaseContainer
215
214
216
- >> > _BagContentType = TypeVar(' _BagContentType ' )
217
- >> > _NewBagContentType = TypeVar(' _NewBagContentType ' )
215
+ >> > _NumberType = TypeVar(' _NumberType ' )
216
+ >> > _NewNumberType = TypeVar(' _NewNumberType ' )
218
217
219
- >> > class Bag (
218
+ >> > class Number (
220
219
... BaseContainer ,
221
- ... SupportsKind1[' Bag ' , int ],
222
- ... Bindable1[_BagContentType ],
220
+ ... SupportsKind1[' Number ' , _NumberType ],
221
+ ... Bindable1[_NumberType ],
223
222
... ):
224
- ... def __init__ (self , inner_value : _BagContentType ) -> None :
223
+ ... def __init__ (self , inner_value : _NumberType ) -> None :
225
224
... super ().__init__ (inner_value)
226
225
...
227
- ... def bind (
226
+ ... def bind ( # This method is required by Bindable
228
227
... self ,
229
- ... function : Callable[[_BagContentType], ' Bag[_NewBagContentType]' ]
230
- ... ) -> ' Bag[_NewBagContentType]' :
231
- ... return function(self ._inner_value)
228
+ ... function : Kind1[
229
+ ... ' Number' ,
230
+ ... Callable[[_NumberType], ' Number[_NewNumberType]' ],
231
+ ... ],
232
+ ... ) -> ' Number[_NewNumberType]' :
233
+ ... return dekind(function(self ._inner_value))
234
+
235
+ And here's how we can use it:
236
+
237
+ .. code :: python
238
+
239
+ >> > def double (arg : int ) -> Number[int ]:
240
+ ... return Number(arg * 2 )
241
+
242
+ >> > number = Number(5 )
243
+ >> > assert number.bind(double) == Number(10 )
244
+
245
+
246
+ Applicative
247
+ -----------
248
+
249
+ .. currentmodule :: returns.interfaces.applicative
250
+
251
+ Something is considered applicative
252
+ if it is a functor already and,
253
+ moreover, we can ``apply `` another container to it
254
+ and construct a new value with ``.from_value `` method.
232
255
233
- >> > @ dataclass
234
- ... class Peanuts (object ):
235
- ... quantity: int
256
+ An example in this library is :class: `~returns.maybe.Maybe `,
257
+ that implements the ``Mappable `` and ``Applicative `` interfaces:
258
+
259
+ .. code :: python
260
+
261
+ >> > from returns.maybe import Maybe, Some
236
262
237
- >> > def get_half ( peanuts : Peanuts) -> Bag[Peanuts]:
238
- ... return Bag(Peanuts(peanuts.quantity // 2 ))
263
+ >> > maybe_str = Maybe.from_value( ' example ' )
264
+ >> > maybe_func = Maybe.from_value( len ) # we use function as a value!
239
265
240
- >> > bag_of_peanuts: Bag[Peanuts] = Bag(Peanuts(10 ))
241
- >> > assert bag_of_peanuts.bind(get_half) == Bag(Peanuts(5 ))
266
+ >> > assert maybe_str.apply(maybe_func) == Some(7 )
267
+
268
+ As you see, ``apply `` takes a container with a function inside
269
+ and applies it to the currect value inside the container.
270
+
271
+ This way we really execute ``Maybe.from_value(len('example')) ``.
272
+
273
+ :class: `~ApplicativeN ` which is a subtype of
274
+ :class: `~returns.interfaces.mappable.MappableN ` interface helps us to
275
+ create our own applicative container like :class: `~returns.maybe.Maybe `.
276
+
277
+ .. code :: python
278
+
279
+ >> > from typing import Callable, TypeVar
280
+
281
+ >> > from returns.interfaces.applicative import Applicative1
282
+ >> > from returns.primitives.hkt import SupportsKind1, Kind1, dekind
283
+ >> > from returns.primitives.container import BaseContainer
284
+
285
+ >> > _NumberType = TypeVar(' _NumberType' )
286
+ >> > _NewNumberType = TypeVar(' _NewNumberType' )
287
+
288
+ >> > class Number (
289
+ ... BaseContainer ,
290
+ ... SupportsKind1[' Number' , _NumberType],
291
+ ... Applicative1[_NumberType],
292
+ ... ):
293
+ ... def __init__ (self , inner_value : _NumberType) -> None :
294
+ ... super ().__init__ (inner_value)
295
+ ...
296
+ ... def map ( # This method is required by Mappable
297
+ ... self ,
298
+ ... function : Callable[[_NumberType], _NewNumberType]
299
+ ... ) -> ' Number[_NewNumberType]' :
300
+ ... return Number(function(self ._inner_value))
301
+ ...
302
+ ... def apply ( # This method is required by Applicative
303
+ ... self ,
304
+ ... container : Kind1[
305
+ ... ' Number' ,
306
+ ... Callable[[_NumberType], _NewNumberType],
307
+ ... ],
308
+ ... ) -> ' Number[_NewNumberType]' :
309
+ ... return Number.from_value(
310
+ ... dekind(container._inner_value(self ._inner_value)),
311
+ ... )
312
+ ...
313
+ ... @ classmethod
314
+ ... def from_value ( # This method is required by Applicative
315
+ ... cls ,
316
+ ... inner_value : _NewNumberType,
317
+ ... ) -> ' Number[_NewNumberType]' :
318
+ ... return Number(inner_value)
319
+
320
+ With our ``Number `` mappable class we can compose easily math functions with it.
321
+
322
+ .. code :: python
323
+
324
+ >> > def my_math_function (number : int ) -> int :
325
+ ... return number - 1
326
+
327
+ >> > number = Number(3 )
328
+ >> > number_function = Number.from_value(my_math_function)
329
+
330
+ >> > assert number.apply(number_function) == Number(2 )
331
+
332
+ Laws
333
+ ~~~~
334
+
335
+ To make sure your ``Applicative `` implementation is right,
336
+ you can apply the ``Applicative `` laws on it to test.
337
+
338
+ 1. :func: `Identity Law <_LawSpec.identity_law> `:
339
+ When we pass an applicative instance
340
+ with wrapped identity function to the ``apply `` method,
341
+ the ``Applicative `` has to be the same, unchanged.
342
+
343
+ .. code :: python
344
+
345
+ >> > from returns.functions import identity
346
+
347
+ >> > applicative_number: Number[int ] = Number(1 )
348
+ >> > assert applicative_number.apply(
349
+ ... applicative_number.from_value(identity),
350
+ ... ) == Number(1 )
351
+
352
+ 2. :func: `Interchange Law <_LawSpec.interchange_law> `:
353
+ We can start our composition with both raw value and a function.
354
+
355
+ .. code :: python
356
+
357
+ >> > def function (arg : int ) -> int :
358
+ ... return arg + 1
359
+
360
+ >> > raw_value = 5
361
+
362
+ >> > assert Number.from_value(raw_value).apply(
363
+ ... Number.from_value(function),
364
+ ... ) == Number.from_value(function).apply(
365
+ ... Number.from_value(lambda inner : inner(raw_value)),
366
+ ... )
367
+
368
+ 3. :func: `Homomorphism Law <_LawSpec.homomorphism_law> `:
369
+ The homomorphism law says that
370
+ applying a wrapped function to a wrapped value is the same
371
+ as applying the function to the value in the normal way
372
+ and then using ``.from_value `` on the result.
373
+
374
+ .. code :: python
375
+
376
+ >> > def function (arg : int ) -> int :
377
+ ... return arg + 1
378
+
379
+ >> > raw_value = 5
380
+
381
+ >> > assert Number.from_value(
382
+ ... function(raw_value),
383
+ ... ) == Number.from_value(raw_value).apply(
384
+ ... Number.from_value(function),
385
+ ... )
386
+
387
+ 4. :func: `Composition Law <_LawSpec.composition_law> `:
388
+ Appling two functions twice is the same
389
+ as applying their composition once.
390
+
391
+ .. code :: python
392
+
393
+ >> > from returns.functions import compose
394
+
395
+ >> > def first (arg : int ) -> int :
396
+ ... return arg * 2
397
+
398
+ >> > def second (arg : int ) -> int :
399
+ ... return arg + 1
400
+
401
+ >> > instance = Number(5 )
402
+ >> > assert instance.apply(
403
+ ... Number.from_value(compose(first, second)),
404
+ ... ) == instance.apply(
405
+ ... Number.from_value(first),
406
+ ... ).apply(
407
+ ... Number.from_value(second),
408
+ ... )
409
+
410
+ Plus all laws from ``MappableN `` interface.
411
+
412
+
413
+ Container
414
+ ---------
415
+
416
+ .. currentmodule :: returns.interfaces.container
417
+
418
+ :class: `~ContainerN ` is a central piece of our library.
419
+ It is an interface that combines
420
+ :class: `~returns.interfaces.applicative.ApplicativeN `
421
+ and
422
+ :class: `~returns.interfaces.bindable.BindableN ` together.
423
+
424
+ So, in other words: ``Container `` is an ``Apllicative `` that you can ``bind ``!
425
+
426
+ .. code :: python
427
+
428
+ >> > from typing import Callable, TypeVar
429
+
430
+ >> > from returns.interfaces.container import Container1
431
+ >> > from returns.primitives.hkt import SupportsKind1, Kind1, dekind
432
+ >> > from returns.primitives.container import BaseContainer
433
+
434
+ >> > _NumberType = TypeVar(' _NumberType' )
435
+ >> > _NewNumberType = TypeVar(' _NewNumberType' )
436
+
437
+ >> > class Number (
438
+ ... BaseContainer ,
439
+ ... SupportsKind1[' Number' , _NumberType],
440
+ ... Container1[_NumberType],
441
+ ... ):
442
+ ... def __init__ (self , inner_value : _NumberType) -> None :
443
+ ... super ().__init__ (inner_value)
444
+ ...
445
+ ... def map ( # This method is required by Mappable
446
+ ... self ,
447
+ ... function : Callable[[_NumberType], _NewNumberType]
448
+ ... ) -> ' Number[_NewNumberType]' :
449
+ ... return Number(function(self ._inner_value))
450
+ ...
451
+ ... def bind ( # This method is required by Bindable
452
+ ... self ,
453
+ ... function : Kind1[
454
+ ... ' Number' ,
455
+ ... Callable[[_NumberType], ' Number[_NewNumberType]' ],
456
+ ... ],
457
+ ... ) -> ' Number[_NewNumberType]' :
458
+ ... return dekind(function(self ._inner_value))
459
+ ...
460
+ ... def apply ( # This method is required by Applicative
461
+ ... self ,
462
+ ... container : Kind1[
463
+ ... ' Number' ,
464
+ ... Callable[[_NumberType], _NewNumberType],
465
+ ... ],
466
+ ... ) -> ' Number[_NewNumberType]' :
467
+ ... return Number.from_value(
468
+ ... container._inner_value(self ._inner_value),
469
+ ... )
470
+ ...
471
+ ... @ classmethod
472
+ ... def from_value ( # This method is required by Applicative
473
+ ... cls ,
474
+ ... inner_value : _NewNumberType,
475
+ ... ) -> ' Number[_NewNumberType]' :
476
+ ... return Number(inner_value)
477
+
478
+ This code gives us an opportunity to use ``Number ``
479
+ with ``map ``, ``apply ``, and ``bind `` as we already did in the examples above.
242
480
243
481
Laws
244
482
~~~~
@@ -252,19 +490,19 @@ respect three new laws.
252
490
253
491
.. code :: python
254
492
255
- >> > def can_be_bound (value : int ) -> Bag[Peanuts ]:
256
- ... return Bag(Peanuts( value) )
493
+ >> > def can_be_bound (value : int ) -> Number[ int ]:
494
+ ... return Number( value)
257
495
258
- >> > assert Bag (5 ).bind(can_be_bound) == can_be_bound(5 )
496
+ >> > assert Number.from_value (5 ).bind(can_be_bound) == can_be_bound(5 )
259
497
260
498
2. :func: `Right Identity <_LawSpec.right_identity_law> `:
261
499
If we pass the bindable constructor through ``bind `` must
262
500
have to be the same result as instantiating the bindable on our own.
263
501
264
502
.. code :: python
265
503
266
- >> > bag = Bag(Peanuts( 2 ) )
267
- >> > assert bag .bind(Bag ) == Bag(Peanuts( 2 ) )
504
+ >> > number = Number( 2 )
505
+ >> > assert number .bind(Number ) == Number( 2 )
268
506
269
507
3. :func: `Associative Law <_LawSpec.associative_law> `:
270
508
Given two functions, ``x `` and ``y ``, calling the bind
@@ -275,31 +513,18 @@ respect three new laws.
275
513
276
514
.. code :: python
277
515
278
- >> > def minus_one (peanuts : Peanuts ) -> Bag[Peanuts ]:
279
- ... return Bag(Peanuts(peanuts.quantity - 1 ) )
516
+ >> > def minus_one (arg : int ) -> Number[ int ]:
517
+ ... return Number(arg - 1 )
280
518
281
- >> > def half (peanuts : Peanuts ) -> Bag[Peanuts ]:
282
- ... return Bag(Peanuts(peanuts.quantity // 2 ) )
519
+ >> > def half (arg : int ) -> Number[ int ]:
520
+ ... return Number(arg // 2 )
283
521
284
- >> > bag = Bag(Peanuts( 9 ) )
285
- >> > assert bag .bind(minus_one).bind(half) == bag .bind(
522
+ >> > number = Number( 9 )
523
+ >> > assert number .bind(minus_one).bind(half) == number .bind(
286
524
... lambda value : minus_one(value).bind(half),
287
525
... )
288
526
289
-
290
- Applicative
291
- -----------
292
-
293
- .. currentmodule :: returns.interfaces.applicative
294
-
295
- Laws
296
- ~~~~
297
-
298
-
299
- Container
300
- ---------
301
-
302
- .. currentmodule :: returns.interfaces.container
527
+ Plus all laws from ``MappableN `` and ``ApplicativeN `` interfaces.
303
528
304
529
305
530
More!
0 commit comments