None values are values that represent the absence of a value. This leads to a proliferation of conditionals and special cases in the code, and is a terrible way to represent the absence of a value. We need something better. Enter the maybe monad.
For a detailed case against None values, read
Consider the following code snippet where we print a value if it is not None.
def print_unless_none(x): if x is not None: print(x) foo = 1 bar = None print_unless_none(foo) print_unless_none(bar)
Rewriting the same code using the maybe monad, we can avoid the conditional check making the code more concise and more robust against bugs.
from pymonad.maybe import Just, Nothing foo = Just(1) bar = Nothing foo.bind(print) bar.bind(print)
Monads may also be passed through a chain of function calls without any condition checking in between. If foo were Nothing, the entire sequence of operations would be skipped with no error raised. Notice how this is much cleaner than interleaving the code with if conditions checking for None intermediate values.
foo = Just(1) foo.map(lambda x: 1 + x) \ .map(lambda x: x**2) \ .bind(print)
Finally, let's put all this together in a practical example using sql_query_mdict from genenetwork. Consider the following code using the DictCursor. The column foo may contain NULL values, and we need to check for them.
with database_connection() as conn: with conn.cursor(MySQLdb.cursors.DictCursor) as cursor: cursor.execute("SELECT foo FROM bar") for row in cursor.fetchall(): if row["foo"] is not None: print(row["foo"])
But, with sql_query_mdict, the row object is a MonadictDict where all values are monadic. We therefore do not need any special conditional checks.
with database_connection() as conn: for row in sql_query_mdict(conn, "SELECT foo FROM bar"): row["foo"].bind(print)
As a bonus, sql_query_mdict also gets rid of cursors by returning a generator and letting us iterate over it pythonically.