aboutsummaryrefslogtreecommitdiff
path: root/gn2/utility/monads.py
diff options
context:
space:
mode:
Diffstat (limited to 'gn2/utility/monads.py')
-rw-r--r--gn2/utility/monads.py114
1 files changed, 114 insertions, 0 deletions
diff --git a/gn2/utility/monads.py b/gn2/utility/monads.py
new file mode 100644
index 00000000..2d708261
--- /dev/null
+++ b/gn2/utility/monads.py
@@ -0,0 +1,114 @@
+"""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)