I have occasionally encountered a pattern in code which resembles a monad but does not keep a consistent type across >>=.

Here is the simplest example I could come up with:

(First some type-level booleans:

data TyT = TyT
data TyF = TyF

class TyOr a b c | a b -> c

instance TyOr TyF TyF TyF
-- rest similarly

)

Now here is our "monad" type constructor:

data Marked p a = Marked a
    deriving (Show)

For a given p, Marked p is a * -> * which acts very much like m in a monad, but different, as occurs next, when we define "bind":

(>>%) :: (TyOr p q r) => Marked p a -> (a -> Marked q b) -> Marked r b
(Marked x) >>% f = Marked y where Marked y = f x

What's different here is that the result of >>% has a different type constructor than the arguments. Other than that it's basically a monad.

We could use it like this:

a :: Marked TyF Int
a = Marked 5

f :: Int -> Marked TyT Int
f x = Marked (x + 1)

ghci> a >>% f
Marked 6

ghci> :t a >>% f
a >>% f :: Marked TyT Int

(This was inspired by outis's observation that Python's "with" can't be a monad because it changes the type, but I've seen it in other (simpler) ways too).

Edit
Report