-
Notifications
You must be signed in to change notification settings - Fork 5
/
funutils.py
98 lines (83 loc) · 2.53 KB
/
funutils.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
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
"""
.. automodule:: fdl
:members:
:platform: Windows
:synopsis: Utilities for functional programming in Python.
.. moduleauthor:: EventHelix.com Inc.
"""
from typing import TypeVar, Generic, NamedTuple, Iterable
a = TypeVar('a')
class Maybe(NamedTuple('Maybe', [('hasValue', bool) , ('value', a)]), Generic[a]):
"""
A container of 0 or 1 element.
"""
def __repr__(self) -> str :
"""
Returns a printable string for the Maybe class.
"""
if self.hasValue:
return f'just({repr(self.value)})'
else:
return 'nothing()'
def map(self, f) :
"""
Applies the given function to the contained value if it is present.
This follows the law,
xs.map(f).map(g) ≡ xs.map(compose(f, g))
, when f and g are pure functions
"""
if self.hasValue:
return just(f(self.value))
else: return nothing()
def do(self, f):
"""
Facilitate function chaining in Maybe.
"""
if self.hasValue :
f(self.value)
return self
else : return self
# (xs >>= f) >>= g ≡ x >>= \ x -> f(x) >>= g
def then(self, f) :
"""
Chain potentially failing computations without a nested if statement.
xs.then(just) ≡ xs
xs.then(lambda x: f(x).then(g)) ≡ xs.then(g).then(g)
where f and g are pure functions
"""
if self.hasValue :
return f(self.value)
else : nothing()
def nothing() -> Maybe[a] :
"""
Construct and empty Maybe.
"""
return Maybe(False, None)
def just(x : a) -> Maybe[a] :
"""
Construct a Maybe with value
"""
return Maybe(True, x)
def first(xs : Iterable[a], condition = lambda x: True) -> Maybe[a]:
"""
Returns the first item in the `iterable` that
satisfies the `condition`.
If the condition is not given, returns the first item of
the iterable.
Raises `StopIteration` if no item satisfying the condition is found.
>>> first( (1,2,3), condition=lambda x: x % 2 == 0)
2
>>> first(range(3, 100))
3
>>> first( () )
Traceback (most recent call last):
...
StopIteration
"""
try:
return just(next(x for x in xs if condition(x)))
except StopIteration:
return nothing()