"""Monadic utilities This module is a collection of monadic utilities for use in GeneNetwork. It includes: * MonadicDict - monadic version of the built-in dictionary * MonadicDictCursor - monadic version of MySQLdb.cursors.DictCursor that returns a MonadicDict instead of the built-in dictionary """ from collections import UserDict from functools import partial from MySQLdb.cursors import DictCursor from pymonad.maybe import Just, Nothing class MonadicDict(UserDict): """ Monadic version of the built-in dictionary. Keys in this dictionary can be any python object, but values must be monadic values. from pymonad.maybe import Just, Nothing Initialize by setting individual keys to monadic values. >>> d = MonadicDict() >>> d["foo"] = Just(1) >>> d["bar"] = Nothing >>> d {'foo': 1} Initialize by converting a built-in dictionary object. >>> MonadicDict({"foo": 1}) {'foo': 1} >>> MonadicDict({"foo": 1, "bar": None}) {'foo': 1} Initialize from a built-in dictionary object with monadic values. >>> MonadicDict({"foo": Just(1)}, convert=False) {'foo': 1} >>> MonadicDict({"foo": Just(1), "bar": Nothing}, convert=False) {'foo': 1} Get values. For non-existent keys, Nothing is returned. Else, a Just value is returned. >>> d["foo"] Just 1 >>> d["bar"] Nothing Convert MonadicDict object to a built-in dictionary object. >>> d.data {'foo': 1} >>> type(d) <class 'utility.monads.MonadicDict'> >>> type(d.data) <class 'dict'> Delete keys. Deleting non-existent keys does nothing. >>> del d["bar"] >>> d {'foo': 1} >>> del d["foo"] >>> d {} """ def __init__(self, d={}, convert=True): """Initialize monadic dictionary. If convert is False, values in dictionary d must be monadic. If convert is True, values in dictionary d are converted to monadic values. """ if convert: super().__init__({key:Just(value) for key, value in d.items() if value is not None}) else: super().__init__(d) def __getitem__(self, key): """Get key from dictionary. If key exists in the dictionary, return a Just value. Else, return Nothing. """ try: return Just(self.data[key]) except KeyError: return Nothing def __setitem__(self, key, value): """Set key in dictionary. value must be a monadic value---either Nothing or a Just value. If value is a Just value, set it in the dictionary. If value is Nothing, do nothing. """ value.bind(partial(super().__setitem__, key)) def __delitem__(self, key): """Delete key from dictionary. If key exists in the dictionary, delete it. Else, do nothing. """ try: super().__delitem__(key) except KeyError: pass def sql_query_mdict(conn, query): """Execute SQL query and return a generator of MonadicDict objects.""" with conn.cursor(DictCursor) as cursor: cursor.execute(query) while (row := cursor.fetchone()): yield MonadicDict(row)