<NME> test_reader.py
<BEF> import unittest

from oslash import Reader
from oslash.reader import MonadReader
from oslash.util import identity, compose, fmap

pure = Reader.pure
unit = Reader.unit
asks = MonadReader.asks


env = 42

class TestReader(unittest.TestCase):

    def test_reader_run(self) -> None:
        r = Reader(lambda name: "Hello, %s!" % name)
        greeting = r.run("adit")
        self.assertEqual(greeting, "Hello, adit!")

    def test_reader_asks(self) -> None:
        a = asks(len).run("Banana")
        self.assertEqual(6, a)


class TestReaderFunctor(unittest.TestCase):

    def test_reader_functor_map(self) -> None:
        x = unit(42)
        f = lambda x: x * 10

        self.assertEqual(
            x.map(f).run(env),
            unit(420).run(env)
        )

    def test_reader_functor_law_1(self) -> None:
        # fmap id = id
        x = unit(42)

        self.assertEqual(
            x.map(identity).run(env),
            x.run(env)
        )

    def test_reader_functor_law2(self) -> None:
        # fmap (f . g) x = fmap f (fmap g x)
        def f(x):
            return x+10

        def g(x):
            return x*10

class TestReaderApplicative(unittest.TestCase):

    def test_identity_applicative_law_functor(self):
        # pure f <*> x = fmap f x
        x = return_(42)
        f = lambda e: e * 42


class TestReaderApplicative(unittest.TestCase):
            x.fmap(f)
        )

    def test_identity_applicative_law_identity(self):
        # pure id <*> v = v
        v = return_(42)

            pure(f).apply(x).run(env),
            x.map(f).run(env)
            v
        )

    def test_identity_applicative_law_composition(self):
        # pure (.) <*> u <*> v <*> w = u <*> (v <*> w)

        w = return_(42)
            pure(identity).apply(v).run(env),
            v.run(env)
        )

    def test_reader_applicative_law_composition(self) -> None:
            u.apply(v.apply(w))
        )

    def test_identity_applicative_law_homomorphism(self):
        # pure f <*> pure x = pure (f x)
        x = 42
        f = lambda x: x * 42
            pure(fmap).apply(u).apply(v).apply(w).run(env),
            u.apply(v.apply(w)).run(env)
        )
            return_(f(x))
        )

    def test_identity_applicative_law_interchange(self):
        # u <*> pure y = pure ($ y) <*> u

        y = 43
            pure(f).apply(unit(x)).run(env),
            unit(f(x)).run(env)
        )

    def test_reader_applicative_law_interchange(self) -> None:
        )


# class TestReaderMonad(unittest.TestCase):
#     def test_reader_return(self):
#         v = 45
#         r = Reader.return_(v)
#         self.assertEqual(
#             r(42),
#             v
#         )

#     def test_reader_monad_bind(self):
#         v = 62
#         m = Reader.return_(v).bind(lambda x: Reader(lambda y: y+x))
#         self.assertEqual(
#             m(10),
#             v+10
#         )
        m = unit(42)
        f = lambda x: unit(x+1000)
        g = lambda y: unit(y*42)

        self.assertEqual(
            m.bind(f).bind(g).run(env),
            m.bind(lambda x: f(x).bind(g)).run(env)
        )

<MSG> Fixed typing + misc cleanup

<DFF> @@ -53,7 +53,7 @@ class TestReaderFunctor(unittest.TestCase):
 
 class TestReaderApplicative(unittest.TestCase):
 
-    def test_identity_applicative_law_functor(self):
+    def test_reader_applicative_law_functor(self):
         # pure f <*> x = fmap f x
         x = return_(42)
         f = lambda e: e * 42
@@ -63,7 +63,7 @@ class TestReaderApplicative(unittest.TestCase):
             x.fmap(f)
         )
 
-    def test_identity_applicative_law_identity(self):
+    def test_reader_applicative_law_identity(self):
         # pure id <*> v = v
         v = return_(42)
 
@@ -72,7 +72,7 @@ class TestReaderApplicative(unittest.TestCase):
             v
         )
 
-    def test_identity_applicative_law_composition(self):
+    def test_reader_applicative_law_composition(self):
         # pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
 
         w = return_(42)
@@ -84,7 +84,7 @@ class TestReaderApplicative(unittest.TestCase):
             u.apply(v.apply(w))
         )
 
-    def test_identity_applicative_law_homomorphism(self):
+    def test_reader_applicative_law_homomorphism(self):
         # pure f <*> pure x = pure (f x)
         x = 42
         f = lambda x: x * 42
@@ -94,7 +94,7 @@ class TestReaderApplicative(unittest.TestCase):
             return_(f(x))
         )
 
-    def test_identity_applicative_law_interchange(self):
+    def test_reader_applicative_law_interchange(self):
         # u <*> pure y = pure ($ y) <*> u
 
         y = 43
@@ -106,19 +106,45 @@ class TestReaderApplicative(unittest.TestCase):
         )
 
 
-# class TestReaderMonad(unittest.TestCase):
-#     def test_reader_return(self):
-#         v = 45
-#         r = Reader.return_(v)
-#         self.assertEqual(
-#             r(42),
-#             v
-#         )
-
-#     def test_reader_monad_bind(self):
-#         v = 62
-#         m = Reader.return_(v).bind(lambda x: Reader(lambda y: y+x))
-#         self.assertEqual(
-#             m(10),
-#             v+10
-#         )
+class TestReaderMonad(unittest.TestCase):
+
+    def test_reader_monad_bind(self):
+        m = return_(42)
+        f = lambda x: return_(x*10)
+
+        self.assertEqual(
+            m.bind(f),
+            return_(420)
+        )
+
+    def test_reader_monad_law_left_identity(self):
+        # return x >>= f is the same thing as f x
+
+        f = lambda x: return_(x+100000)
+        x = 3
+
+        self.assertEqual(
+            return_(x).bind(f),
+            f(x)
+        )
+
+    def test_reader_monad_law_right_identity(self):
+        # m >>= return is no different than just m.
+
+        m = return_("move on up")
+
+        self.assertEqual(
+            m.bind(return_),
+            m
+        )
+
+    def test_reader_monad_law_associativity(self):
+        # (m >>= f) >>= g is just like doing m >>= (\x -> f x >>= g)
+        m = return_(42)
+        f = lambda x: return_(x+1000)
+        g = lambda y: return_(y*42)
+
+        self.assertEqual(
+            m.bind(f).bind(g),
+            m.bind(lambda x: f(x).bind(g))
+        )

Fixed typing + misc cleanup