Skip to content

Instantly share code, notes, and snippets.

@mklbtz
Last active December 20, 2018 18:26
Show Gist options
  • Save mklbtz/ea5d8675da493c25412d1ebff2de0965 to your computer and use it in GitHub Desktop.
Save mklbtz/ea5d8675da493c25412d1ebff2de0965 to your computer and use it in GitHub Desktop.
Things I already wish I had in Python
from __future__ import print_function
class NoneError(TypeError):
pass
class maybe:
"""
A two-state monad that represents the presents or absence of a value.
Think of it as a collection of zero or one values.
"""
def __init__(self, wrapped):
self.__wrapped__ = wrapped
def __iter__(self):
if self.__wrapped__ is not None:
yield self.__wrapped__
def map(self, func):
for wrapped in self:
return maybe(func(wrapped))
return maybe(None)
def flatmap(self, func):
return maybe(self.map(func).__wrapped__.__wrapped__)
def unwrapped(self):
if self.__wrapped__ is None:
raise NoneError('Unexpectedly found None when unwrapping')
return self.__wrapped__
@staticmethod
def next(sequence):
return maybe(next(sequence, None))
# Examples
maybe('a string!').map(print) # does print
maybe(None).map(print) # never prints
evens = (x for x in xrange(4) if x % 2 == 0)
empty = (x for x in xrange(4) if x % 2 == 2)
for x in maybe.next(evens):
print('even', x) # does print
for x in maybe.next(empty):
print('broken', x) # never prints
nickname = maybe(None)
nickname.map(lambda n: n.upper()).map(print) # Never capitalizes or prints
someNumbers = maybe([0, None, 2])
try:
someNumbers.flatmap(lambda nums: maybe(nums[1])).unwrapped() # throws NoneError
except:
print("unwrap error!")
def first(predicate, iterable):
"""Returns the first element in `iterable` matching `predicate"""
return next(x for x in iterable if predicate(x))
def last(predicate, iterable):
"""Returns the last element in `iterable` matching `predicate"""
return first(predicate, reversed(iterable))
# Examples
isEven = lambda x: x % 2 == 0
first(isEven, range(4)) # returns 0
last(isEven, range(4)) # returns 2
first(lambda x: x == 2, range(2)) # raises StopIteration
def zipnone(*values):
"""
A corrolary to the standard zip function for working with potential None values.
Use it to verify that all values are present before executing a block of code.
"""
if None in values:
return
else:
yield values
# Examples
for tup in zipnone(1, 2, 3):
print tup # prints (1, 2, 3)
for tup in zipnone(1, 2, None):
assert False, "this should not run since None is present"
def zipfunc(*funcs):
"""
A corrolary to the standard zip function for working with functions.
Converts N unary functions into one N-ary function.
"""
def zipped(*args):
if len(funcs) != len(args):
raise TypeError("zipped function expected {} arguments, got {}".format(len(funcs), len(args)))
return tuple(f(a) for f, a in zip(funcs, args))
return zipped
# Examples
square = lambda a: a * a
square2 = zipfunc(square, square)
a, b = square2(2, 3) # returns (4, 9)
# another example follows
def pipe(*funcs):
"""
Returns a function that represents a unix-style pipeline of the functions passed in.
"""
def apply(result, func):
if isinstance(result, tuple):
return func(*result)
else:
return func(result)
return lambda initial: reduce(apply, funcs, initial)
# Example
class Transaction:
def __init__(self, customer, vendor):
self.customer = customer
self.vendor = vendor
def customer_email(customer):
print "email to {}".format(customer)
def vendor_email(vendor):
print "email to {}".format(vendor)
transactions = [Transaction('Charlie', 'Paul')]
for t in transactions:
customer_email(t.customer)
vendor_email(t.vendor)
# equivalently...
transaction_emails = pipe(
lambda t: (t.customer, t.vendor),
zipfunc(customer_email, vendor_email)
)
map(transaction_emails, transactions)
@fisher6
Copy link

fisher6 commented Dec 20, 2018

👏
print(maybe(xom_will_make_us_rich))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment