I recently learned how monads work and how they can be used to take more control over side effects. I wrote a few to calculate the time of composed functions, and to ofc take care of logging. One annoying thing I noticed is that I had to repeatedly write .bind(func)
instead of simply .func
, which is a slight annoyance.
So I then tried to include that in my logger monad and this is what I ended up with:
class IterBetter:
def __init__(self, value: Iterable[T], log: List[str] = None):
self.value = value
self.log = log or []
self.original_type = type(value)
# convenience methods
def __repr__(self):
return f"{self.value}"
def __getitem__(self, index):
return self.value[index]
def __setitem__(self, index, value):
self.value[index] = value
@staticmethod
def bindify(f):
def wrapper(self, func):
result, msg = f(self, func)
return IterBetter(result, self.log + [msg])
return wrapper
@bindify
def newmap(self, func):
msg = f"Mapping {func} over {self.value}"
mapped = self.original_type(map(func, self.value))
return mapped, msg
@bindify
def newfilter(self, func):
msg = f"Filtering {func} over {self.value}"
filtered = self.original_type(filter(func, self.value))
return filtered, msg
Now you can write something like
mylst = IterBetter([1, 2, 3, 4, 5])
newlst = (
mylst
.newmap(lambda x: x + 1)
.newfilter(lambda x: x % 2 == 0)
.newmap(lambda x: x * 3)
)
Which is very nice imo, it's definitely more convenient than python's built-in maps and filers (but comprehensions exist, so this isn't all that practical).
However... Why would we want to use a design like that for the class? Like what's the advantage of returning a log message instead of just appending it to the log list? Either way we have to change the original log.
And is this modified implementation still a monad?
Aucun commentaire:
Enregistrer un commentaire