Created
January 21, 2020 12:53
-
-
Save Niriel/83699a50914179d41c045d5d06aa8b20 to your computer and use it in GitHub Desktop.
Python method that can't even be accessed if some condition is False
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Dunno how useful it could be. Whatever. | |
import types | |
class Monoid: | |
def __init__(self, xs): | |
self.xs = xs | |
@classmethod | |
def empty(cls): | |
raise NotImplementedError | |
def append(self, x): | |
raise NotImplementedError | |
class Mult(Monoid): | |
# Number multiplication form a monoid of identity 1. | |
@classmethod | |
def empty(cls): | |
return Mult(1) | |
def append(self, x): | |
# xs is a number that may result from previous calls to append. | |
# x is a number. | |
return Mult(self.xs * x) | |
class List(Monoid): | |
# Concatenation is the free monoid in any cartesian category. | |
# The identity element is the empty list. | |
@classmethod | |
def empty(cls): | |
return List([]) | |
def append(self, x): | |
# xs is an actual (potentially empty) list of elements | |
# x is a new element to add to the list | |
return List(self.xs + [x]) | |
class Func(Monoid): | |
# Function composition and identity form a monoid. | |
@classmethod | |
def empty(cls): | |
return Func(lambda a: a) | |
def append(self, x): | |
# xs is a function. | |
# x is also | |
# This is function composition, we run x after a. | |
return Func(lambda a: x(self.xs(a))) | |
class IfMonoid: | |
def __init__(self, method): | |
self.method = method | |
self.name = 'extra' | |
def __get__(self, instance, owner): | |
if instance is None: | |
return self | |
if isinstance(getattr(instance, self.name), Monoid): | |
return types.MethodType(self.method, instance) | |
raise AttributeError | |
class CarryExtra: | |
def __init__(self, extra, value): | |
self.extra = extra | |
self.value = value | |
def map(self, f): | |
# Run f on the value, pass the extra data along, unchanged. | |
return CarryExtra(self.extra, f(self.value)) | |
@IfMonoid | |
def flatmap(self, f): | |
# Here, f takes a value and returns a CarryExtra. | |
# We need to combine that new extra with the current extra. | |
# For that we use a monoid. | |
# If extra isn't a monoid, well, we just don't have a monad at all. | |
y = f(self.value) | |
new_value = y.value | |
new_extra = y.extra | |
return CarryExtra(self.extra.append(new_extra), new_value) | |
def _main(): | |
def inc(x): | |
return x + 1 | |
def dbl(x): | |
return x * 2 | |
def log_inc(x): | |
return CarryExtra('called inc on {}'.format(x), inc(x)) | |
def log_dbl(x): | |
return CarryExtra('called dbl on {}'.format(x), dbl(x)) | |
def log_str(x): | |
return CarryExtra('stringified {}'.format(x), str(x)) | |
print('======== case 1: metadata is not monoid') | |
# Attach some stupid metadata to the value 10, then do maths with it. | |
metadata = {'i like': 'rabbits'} | |
ce0 = CarryExtra(metadata, 10) | |
print('Initial', ce0.extra, ce0.value) | |
print('Trying map...') | |
ce1 = ce0.map(inc) | |
ce2 = ce1.map(dbl) | |
assert ce2.value == 22 | |
assert ce2.extra == metadata | |
print('Trying flatmap...') | |
try: | |
_ = ce2.flatmap | |
except AttributeError: | |
print('[ OK ] I got the AttributeError I expected because metadata is not a monoid') | |
else: | |
print('[FAIL] This should have raised AttributeError because we cannot have that monad without a monoid') | |
print('Final ', ce2.extra, ce2.value) | |
print() | |
print('======== case 2: metadata is a monoid') | |
metadata = List.empty().append('Mushrooms are pretty cool') | |
ce0 = CarryExtra(metadata, 10) | |
print('initial value: ', repr(ce0.value)) | |
print('initial extra: ', ce0.extra.xs) | |
print('Trying map...') | |
ce1 = ce0.map(inc) | |
print('Trying flatmap...') | |
ce2 = ce1.flatmap(log_dbl) | |
ce3 = ce2.flatmap(log_str) | |
print('final value: ', repr(ce3.value)) | |
print('final extra: ', ce3.extra.xs) | |
if __name__ == '__main__': | |
_main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment