Adding a Constraint to a Monad Result Type - haskell

I would like to create a type that is an instance of Monad such that the result type can only be an instance of a specific typeclass. I would like to be able to write something like
data T a = T a
class C a where
...
instance Monad T where
return :: (C a) => a -> m a
return x = ...
(>>=) :: (C a, C b) => m a -> (a -> m b) -> m b
p >>= f = ...
In the actual code that I'm working on, I need a typeclass constraint on the result type so that a specific function from the typeclass is available in the definitions for return and (>>=).
Is there any way to do this?

As per #chi's comment, the best solution is probably to use either constrained monads or indexed monads. Both solutions have the caveat of having to work around the standard library, but it can be done with workarounds such as rebinding syntax using the RebindableSyntax extension for example. The best example of constrained monads I was able to find was the constrained-monads package available on Hackage, while the best example I was able to find of indexed monads was a post at Kwang's Haskell Blog.

Related

Hidden forall quantified types in ReifiedTraversal

This question really is more generic, since while I was asking it I found out how to fix it in this particular case (even though I don't like it) but I'll phrase it in my particular context.
Context:
I'm using the lens library and I found it particularly useful to provide functionality for "adding" traversals (conceptually, a traversal that traverses all the elements in both original traversals). I did not find a default implementation so I did it using Monoid. In order to be able to implement an instance, I had to use the ReifiedTraversal wrapper, which I assume is in the library precisely for this purpose:
-- Adding traversals
add_traversals :: Semigroup t => Traversal s t a b -> Traversal s t a b -> Traversal s t a b
add_traversals t1 t2 f s = liftA2 (<>) (t1 f s) (t2 f s)
instance Semigroup t => Semigroup (ReifiedTraversal s t a b) where
a1 <> a2 = Traversal (add_traversals (runTraversal a1) (runTraversal a2))
instance Semigroup s => Monoid (ReifiedTraversal' s a) where
mempty = Traversal (\_ -> pure . id)
The immediate application I want to extract from this is being able to provide a traversal for a specified set of indices in a list. Therefore, the underlying semigroup is [] and so is the underlying Traversable. First, I implemented a lens for an individual index in a list:
lens_idx :: Int -> Lens' [a] a
lens_idx _ f [] = error "No such index in the list"
lens_idx 0 f (x:xs) = fmap (\rx -> rx:xs) (f x)
lens_idx n f (x:xs) = fmap (\rxs -> x:rxs) (lens_idx (n-1) f xs)
All that remains to be done is to combine these two things, ideally to implement a function traversal_idxs :: [Int] -> Traversal' [a] a
Problem:
I get type checking errors when I try to use this. I know it has to do with the fact that Traversal is a type that includes a constrained forall quantifier in its definition. In order to be able to use the Monoid instance, I need to first reify the lenses provided by lens_idx (which are, of course, also traversals). I try to do this by doing:
r_lens_idx :: Int -> ReifiedTraversal' [a] a
r_lens_idx = Traversal . lens_idx
But this fails with two errors (two versions of the same error really):
Couldn't match type ‘f’ with ‘f0’...
Ambiguous type variable ‘f0’ arising from a use of ‘lens_idx’
prevents the constraint ‘(Functor f0)’ from being solved...
I understand this has to do with the hidden forall f. Functor f => in the Traversal definition. While writing this, I realized that the following does work:
r_lens_idx :: Int -> ReifiedTraversal' [a] a
r_lens_idx idx = Traversal (lens_idx idx)
So, by giving it the parameter it can make the f explicit to itself and then it can work with it. However, this feels extremely ad-hoc. Specially because originally I was trying to build this r_lens_idx inline in a where clause in the definition of the traversal_idxs function (in fact... on a function defining this function inline because I'm not really going to use it that often).
So, sure, I guess I can always use lambda abstraction, but... is this really the right way to deal with this? It feels like a hack, or rather, that the original error is an oversight by the type-checker.
The "adding" of traversals that you want was added in the most recent lens release, you can find it under the name adjoin. Note that it is unsound to use if your traversals overlap at all.
I am replying to my own question, although it is only pointing out that what I was trying to do with traversals was not actually possible in that shape and how I overcame it. There is still the underlying problem of the hidden forall quantified variables and how is it possible that lambda abstraction can make code that does not type check suddenly type check (or rather, why it did not type check to start with).
It turns out my implementation of Monoid for Traversal was deeply flawed. I realized when I started debugging it. For instance, I was trying to combine a list of indices, and a function that would return a lens for each index, mapping to that index in a list, to a traversal that would map to exactly those indices. That is possible, but it relies on the fact that List is a Monad, instead of just using the Applicative structure.
The function that I had written originally for add_traversal used only the Applicative structure, but instead of mapping to those indices in the list, it would duplicate the list for each index, concatenating them, each version of the list having applied its lens.
When trying to fix it, I realized I needed to use bind to implement what I really wanted, and then I stumbled upon this: https://www.reddit.com/r/haskell/comments/4tfao3/monadic_traversals/
So the answer was clear: I can do what I want, but it's not a Monoid over Traversal, but instead a Monoid over MTraversal. It still serves my purposes perfectly.
This is the resulting code for that:
-- Monadic traversals: Traversals that only work with monads, but they allow other things that rely on the fact they only need to work with monads, like sum.
type MTraversal s t a b = forall m. Monad m => (a -> m b) -> s -> m t
type MTraversal' s a = MTraversal s s a a
newtype ReifiedMTraversal s t a b = MTraversal {runMTraversal :: MTraversal s t a b}
type ReifiedMTraversal' s a = ReifiedMTraversal s s a a
-- Adding mtraversals
add_mtraversals :: Semigroup t => MTraversal r t a b -> MTraversal s r a b -> MTraversal s t a b
add_mtraversals t1 t2 f s = (t2 f s) >>= (t1 f)
instance Semigroup s => Semigroup (ReifiedMTraversal' s a) where
a1 <> a2 = MTraversal (add_mtraversals (runMTraversal a1) (runMTraversal a2))
instance Semigroup s => Monoid (ReifiedMTraversal' s a) where
mempty = MTraversal (\_ -> return . id)
Note that MTraversal is still a LensLike and an ASetter, so you can use many operators from the lens package, like .~.
As I mentioned, though, I still have to use lambda abstraction when using this for my purposes due to the forall quantifier being in an uncomfortable place, and I'd love if someone could clarify what the heck is up with the type checker in that regard.

How could we know that an applicative can't be a Monad?

From the example of Validation (https://hackage.haskell.org/package/Validation), I'm trying to get an intuition of detecting how/why an applicative could not be a Monad (Why can AccValidation not have a Monad instance?)
Could you challenge my reasoning ?
I think about a monad in the way we handle behind the join (m ( m b) -> m b), let's develop my understanding with an example like Validation:
in data Validation err a, the functor structure is (Validation err). When you look at the definition of the bind for Monad and specializing the types for Validation you get the following :
(>>=) :: m a -> (a -> m b) -> m b
(>>=) :: (Validation err) a -> ( a -> (Validation err) b) -> (Validation err) b
if you beta reduce (>>=) you'll get :
m a -> (a -> m b) -> m b // if we apply (m a) in the monadic function
m ( m b) -> m b
then to get the result of (>>=) which is m b, you'll use join :
join :: (Monad m) => m (m a) -> m a
join x = x >>= id
If you play with the types you'll get :
join m ( m b ) = m ( m b) >>= (\(m b) -> m b -> m b) which gives m b
So that join just drop the outermost structure, only the value in the innermost type (the value of the innermost functor) is kept/transmitted through the sequence.
In a monad we can't pass some information from the functor structure (e.g Validation err) to the next 'action', the only think we can pass is the value. The only think you could do with that structure is short-circuiting the sequence to get information from it.
You can't perform a sequence of action on the information from the functor structure (e.g accumulating something like error..)
So I would say that an applicative that is squashing its structure with some logic on its structure could be suspicious as not being able to become a Monad ?
This isn't really an answer, but it's too long for a comment.
This and other referenced discussions in that thread are relevant. I think the question is posed sort of backwards: all Monads naturally give rise to an Applicative (where pure = return, etc); the issue is that most users expect/assume that (where a type is instance Monad) the Applicative instance is semantically equivalent to the instance to which the Monad gives rise.
This is documented in the Applicative class as a sort of law, but I'm not totally convinced it's justified. The argument seems to be that having an Applicative and Monad that don't agree in this way is confusing.
My experience using Validation is that it's a nightmare to do anything large with it, both because the notation becomes a mess and because you find you have some data dependencies (e.g. you need to parse and validate one section based on the parse of a previous section). You end up defining bindV which behave like an Error monad >>= since a proper Monad instance is considered dubious.
And yet using a Monad/Applicative pair like this does what you want: especially when using ApplicativeDo (I imagine; haven't tried this), the effect of writing your parser (e.g.) in Monadic style is that you can accumulate as many errors as possible at every level, based on the data dependencies of your parsing code. Haxl arguably fudges this "law" in a similar way.
I don't have enough experience with other types that are Applicative but not Monad to know whether there's a sensible rule for when it's "okay" for the Applicative to disagree in this way. Maybe it's totally arbitrary that Validation seems to work sensibly.
In any case...
I'm not sure how to directly answer your question. I think you start by taking the laws documented at the bottom of Applicative class docs, and flip them, so you get:
return = pure
ap m1 m2 = m1 <*> m2
If ap were a method of Monad and the above was a minimal complete definition then you'd simply have to test whether the above passed the Monad laws to answer your question for any Applicative, but that's not the case of course.

How does an environment happen to be a partially applied function, and even a hom functor?

I have seen Reader being used to major benefit many times in the wild. (One notable example would be stack, built around a straightforward derivative of Reader that can inform the user of the sufficiency of its contents on the type level.) After some thinking, I arrived to an understanding that this benefit is merely on the level of code structure, and, in a sense, all that Reader does is supply a parameter, in many places, to a complicated wiring of functions. That is, I came to believe we can always replace a reader that holds some x with a lambda abstraction of form λx. ... x ... x .... This seems to align with the official explanations that claim:
... the partially applied function type (->) r is a simple reader monad ...
However, there is a long way from noting that Reader is a way to write down a lambda abstraction piecewise, to claiming that it is a partially applied function.
Is there a function that is applied, but not partially? It would simply be a value:
λ :t id 1
id 1 :: Num a => a
λ :t 1
1 :: Num a => a
Is there a function that is not even partially applied? We'll never know:
λ :t fromMaybe
fromMaybe :: a -> Maybe a -> a
λ :t flip maybe id
flip maybe id :: b -> Maybe b -> b
Even ignoring this as nitpicking, I would not take on faith that (->) r (why not just write (r ->)?) is exactly and singularly a Reader monad. Maybe I could write an instance that typechecks. Maybe it would even obey the laws. As long as I do not think of my functions as Readers, as long as I do not have the right intuition, the vision of it, it is as useful to me as the first proof of the Four Colour Theorem. On the other hand, how can I be sure this is the only way of defining a monad on functions? There are several Monoids on Num, at least two Applicatives on Lists − would it not be too reckless to consider a function a Reader monad alone?
The predicament does not end here. Once I go searching for an answer, I stumble upon an even more puzzling note: Reader happens to be the hom functor now, or even a representable functor in general. And the folks from the other end of the spectrum actually know ahead of time that there would be such a construct in Haskell, and even spell its type, the same as it is spelled in the aforementioned official explanations. Now, this is way over my head, but I can parse the definition of hom functor from Mac Lane. With some imagination, it can be seen that, granted a function a -> b as a morphism in the (supposed) category Hask, we may compose it with id to obtain... a function a -> b again, this time as an element of the set hom(a, b).
Does this connect in any way with some of these morphisms being partially applied? Or with the use of Reader as option store in stack? Can I actually be shown the object and arrow functions of the hom functor Hask -> Set? (I shall take an endofunctor Hask -> Hask as a reasonable approximation.) Would that be my trusty fellows pure and fmap?
And, well, how do I actually use Reader, after that?
I can't answer all of your questions, but let's start with the easy ones:
(->) r (why not just write (r ->)?)
Because the latter is a syntax error in Haskell. You can't use this section syntax on types.
... claiming that it is a partially applied function.
That's not what it's saying. The quote is:
the partially applied function type (->) r is a simple reader monad
It's a partially applied type, not a partially applied function. Or in other words, it's parsed like this: ((partially applied) (function type))
The type constructor for function types in Haskell is spelled ->.
A fully applied function type looks like r -> a (or equivalently (->) r a), where r is the argument type and a the result type.
Thus (->) r is a partial application of -> (the function type).
If we ignore monad transformers and ReaderT, the straightforward definition of Reader is:
newtype Reader r a = Reader (r -> a)
runReader :: Reader r a -> r -> a
runReader (Reader f) x = f x
-- or rather:
runReader :: Reader r a -> (r -> a)
runReader (Reader f) = f
or equivalently:
newtype Reader r a = Reader{ runReader :: r -> a }
That is, Reader is just a newtype for -> (a very thin wrapper), with runReader for unwrapping.
You can even make -> an instance of MonadReader just by copying the instance for Reader and removing all the Reader / runReader wrapping/unwrapping.

How to abstract over monads without fighting the type system in Haskell?

I'm currently building a server in haskell and as a newbie to the language, I'd like to try a new approach zu Monad composition. The idea is that we can write library methods like
isGetRequest :: (SupportsRequests m r) => m Bool
isGetRequest = do
method <- liftRequests $ requestMethod
return $ method == GET
class (Monad m, RequestSupport r) => SupportsRequests m r | m -> r where
liftRequests :: r a -> m a
class (Monad r) => RequestSupport r where
requestMethod :: r Method
which work without knowing the underlying monad. Of course in this example, it would have been sufficient to make isGetRequest operate directly on the (RequestSupport r) monad but the idea is that my library might also have more than one constraint on the monad. Yet, I do not want to implement all of those different concerns in the same module nor spread them across different modules (orphan instances!).
That's why the m monad only implements the Supports* classes, delegating the real concerns to other monads.
The above code should work perfectly (with some language extensions to GHC). Unfortunately, I got some problems with the CRUD (Create Read Update Delete) concern:
class (Monad m, CRUDSupport c a) => SupportsCRUD m c a | m a -> c where
liftCRUD :: c x -> m x
class (Monad c) => CRUDSupport c a | c -> a where
list :: c [a] -- List all entities of type a
No I get an error:
Could not deduce (SupportsCRUD m c a0) from the context [...]
The type variable 'a0' is ambiguous [...]
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method: liftCRUD [...]
Seems like the type checker doesn't like that the a parameter does not directly arise in the signature of liftCRUD. That's understandable because a cannot be derived from the functional dependencies.
The type checker in my brain tells me that it should not be a problem to infer the type a later on, using AllowAmbiguousTypes, when some method regarding CRUD is executed in a library method. Unfortunately, GHC seems unable to do this inference step, for example
bookAvailable :: (SupportsCRUD m c Book) => m Bool
bookAvailable = do
books <- liftCRUD (list :: c [Book]) -- I use ScopedTypeVariables
case books of
[] -> return False
_ -> return True
yields
Could not deduce (SupportsCRUD m c0 a1) arising from a use of 'liftCRUD' [...]
The type variables c0, a1 are ambiguous [...]
It seems that I am still unable to reason about the compiler. I there a way to resolve this problem? Or at least a way to understand what the compiler is able to infer?
Best Regards,
bloxx
To use ScopedTypeVariables you also need to bind the variables you want to be in scope with forall. So it should be
bookAvailable :: forall m c. (SupportsCRUD m c Book) => m Bool
...
That was all that was necessary (after some trivial fixes I made which I assume were typos from entering your question) for me to get the code to compile.

Monad theory and Haskell

Most tutorials seem to give a lot of examples of monads (IO, state, list and so on) and then expect the reader to be able to abstract the overall principle and then they mention category theory. I don't tend to learn very well by trying generalise from examples and I would like to understand from a theoretical point of view why this pattern is so important.
Judging from this thread:
Can anyone explain Monads?
this is a common problem, and I've tried looking at most of the tutorials suggested (except the Brian Beck videos which won't play on my linux machine):
Does anyone know of a tutorial that starts from category theory and explains IO, state, list monads in those terms? the following is my unsuccessful attempt to do so:
As I understand it a monad consists of a triple: an endo-functor and two natural transformations.
The functor is usually shown with the type:
(a -> b) -> (m a -> m b)
I included the second bracket just to emphasise the symmetry.
But, this is an endofunctor, so shouldn't the domain and codomain be the same like this?:
(a -> b) -> (a -> b)
I think the answer is that the domain and codomain both have a type of:
(a -> b) | (m a -> m b) | (m m a -> m m b) and so on ...
But I'm not really sure if that works or fits in with the definition of the functor given?
When we move on to the natural transformation it gets even worse. If I understand correctly a natural transformation is a second order functor (with certain rules) that is a functor from one functor to another one. So since we have defined the functor above the general type of the natural transformations would be:
((a -> b) -> (m a -> m b)) -> ((a -> b) -> (m a -> m b))
But the actual natural transformations we are using have type:
a -> m a
m a -> (a ->m b) -> m b
Are these subsets of the general form above? and why are they natural transformations?
Martin
A quick disclaimer: I'm a little shaky on category theory in general, while I get the impression you have at least some familiarity with it. Hopefully I won't make too much of a hash of this...
Does anyone know of a tutorial that starts from category theory and explains IO, state, list monads in those terms?
First of all, ignore IO for now, it's full of dark magic. It works as a model of imperative computations for the same reasons that State works for modelling stateful computations, but unlike the latter IO is a black box with no way to deduce the monadic structure from the outside.
The functor is usually shown with the type: (a -> b) -> (m a -> m b) I included the second bracket just to emphasise the symmetry.
But, this is an endofunctor, so shouldn't the domain and codomain be the same like this?:
I suspect you are misinterpreting how type variables in Haskell relate to the category theory concepts.
First of all, yes, that specifies an endofunctor, on the category of Haskell types. A type variable such as a is not anything in this category, however; it's a variable that is (implicitly) universally quantified over all objects in the category. Thus, the type (a -> b) -> (a -> b) describes only endofunctors that map every object to itself.
Type constructors describe an injective function on objects, where the elements of the constructor's codomain cannot be described by any means except as an application of the type constructor. Even if two type constructors produce isomorphic results, the resulting types remain distinct. Note that type constructors are not, in the general case, functors.
The type variable m in the functor signature, then, represents a one-argument type constructor. Out of context this would normally be read as universal quantification, but that's incorrect in this case since no such function can exist. Rather, the type class definition binds m and allows the definition of such functions for specific type constructors.
The resulting function, then, says that for any type constructor m which has fmap defined, for any two objects a and b and a morphism between them, we can find a morphism between the types given by applying m to a and b.
Note that while the above does, of course, define an endofunctor on Hask, it is not even remotely general enough to describe all such endofunctors.
But the actual natural transformations we are using have type:
a -> m a
m a -> (a ->m b) -> m b
Are these subsets of the general form above? and why are they natural transformations?
Well, no, they aren't. A natural transformation is roughly a function (not a functor) between functors. The two natural transformations that specify a monad M look like I -> M where I is the identity functor, and M ∘ M -> M where ∘ is functor composition. In Haskell, we have no good way of working directly with either a true identity functor or with functor composition. Instead, we discard the identity functor to get just (Functor m) => a -> m a for the first, and write out nested type constructor application as (Functor m) => m (m a) -> m a for the second.
The first of these is obviously return; the second is a function called join, which is not part of the type class. However, join can be written in terms of (>>=), and the latter is more often useful in day-to-day programming.
As far as specific monads go, if you want a more mathematical description, here's a quick sketch of an example:
For some fixed type S, consider two functors F and G where F(x) = (S, x) and G(x) = S -> x (It should hopefully be obvious that these are indeed valid functors).
These functors are also adjoints; consider natural transformations unit :: x -> G (F x) and counit :: F (G x) -> x. Expanding the definitions gives us unit :: x -> (S -> (S, x)) and counit :: (S, S -> x) -> x. The types suggest uncurried function application and tuple construction; feel free to verify that those work as expected.
An adjunction gives rise to a monad by composition of the functors, so taking G ∘ F and expanding the definition, we get G (F x) = S -> (S, x), which is the definition of the State monad. The unit for the adjunction is of course return; and you should be able to use counit to define join.
This page does exactly that. I think your main confusion is that the class doesn't really make the Type a functor, but it defines a functor from the category of Haskell types into the category of that type.
Following the notation of the link, assuming F is a Haskell Functor, it means that there is a functor from the category of Hask to the category of F.
Roughly speaking, Haskell does its category theory all in just one category, whose objects are Haskell types and whose arrows are functions between these types. It's definitely not a general-purpose language for modelling category theory.
A (mathematical) functor is an operation turning things in one category into things in another, possibly entirely different, category. An endofunctor is then a functor which happens to have the same source and target categories. In Haskell, a functor is an operation turning things in the category of Haskell types into other things also in the category of Haskell types, so it is always an endofunctor.
[If you're following the mathematical literature, technically, the operation '(a->b)->(m a -> m b)' is just the arrow part of the endofunctor m, and 'm' is the object part]
When Haskellers talk about working 'in a monad' they really mean working in the Kleisli category of the monad. The Kleisli category of a monad is a thoroughly confusing beast at first, and normally needs at least two colours of ink to give a good explanation, so take the following attempt for what it is and check out some references (unfortunately Wikipedia is useless here for all but the straight definitions).
Suppose you have a monad 'm' on the category C of Haskell types. Its Kleisli category Kl(m) has the same objects as C, namely Haskell types, but an arrow a ~(f)~> b in Kl(m) is an arrow a -(f)-> mb in C. (I've used a squiggly line in my Kleisli arrow to distinguish the two). To reiterate: the objects and arrows of the Kl(C) are also objects and arrows of C but the arrows point to different objects in Kl(C) than in C. If this doesn't strike you as odd, read it again more carefully!
Concretely, consider the Maybe monad. Its Kleisli category is just the collection of Haskell types, and its arrows a ~(f)~> b are functions a -(f)-> Maybe b. Or consider the (State s) monad whose arrows a ~(f)~> b are functions a -(f)-> (State s b) == a -(f)-> (s->(s,b)). In any case, you're always writing a squiggly arrow as a shorthand for doing something to the type of the codomain of your functions.
[Note that State is not a monad, because the kind of State is * -> * -> *, so you need to supply one of the type parameters to turn it into a mathematical monad.]
So far so good, hopefully, but suppose you want to compose arrows a ~(f)~> b and b ~(g)~> c. These are really Haskell functions a -(f)-> mb and b -(g)-> mc which you cannot compose because the types don't match. The mathematical solution is to use the 'multiplication' natural transformation u:mm->m of the monad as follows: a ~(f)~> b ~(g)~> c == a -(f)-> mb -(mg)-> mmc -(u_c)-> mc to get an arrow a->mc which is a Kleisli arrow a ~(f;g)~> c as required.
Perhaps a concrete example helps here. In the Maybe monad, you cannot compose functions f : a -> Maybe b and g : b -> Maybe c directly, but by lifting g to
Maybe_g :: Maybe b -> Maybe (Maybe c)
Maybe_g Nothing = Nothing
Maybe_g (Just a) = Just (g a)
and using the 'obvious'
u :: Maybe (Maybe c) -> Maybe c
u Nothing = Nothing
u (Just Nothing) = Nothing
u (Just (Just c)) = Just c
you can form the composition u . Maybe_g . f which is the function a -> Maybe c that you wanted.
In the (State s) monad, it's similar but messier: Given two monadic functions a ~(f)~> b and b ~(g)~> c which are really a -(f)-> (s->(s,b)) and b -(g)-> (s->(s,c)) under the hood, you compose them by lifting g into
State_s_g :: (s->(s,b)) -> (s->(s,(s->(s,c))))
State_s_g p s1 = let (s2, b) = p s1 in (s2, g b)
then you apply the 'multiplication' natural transformation u, which is
u :: (s->(s,(s->(s,c)))) -> (s->(s,c))
u p1 s1 = let (s2, p2) = p1 s1 in p2 s2
which (sort of) plugs the final state of f into the initial state of g.
In Haskell, this turns out to be a bit of an unnatural way to work so instead there's the (>>=) function which basically does the same thing as u but in a way that makes it easier to implement and use. This is important: (>>=) is not the natural transformation 'u'. You can define each in terms of the other, so they're equivalent, but they're not the same thing. The Haskell version of 'u' is written join.
The other thing missing from this definition of Kleisli categories is the identity on each object: a ~(1_a)~> a which is really a -(n_a)-> ma where n is the 'unit' natural transformation. This is written return in Haskell, and doesn't seem to cause as much confusion.
I learned category theory before I came to Haskell, and I too have had difficulty with the mismatch between what mathematicians call a monad and what they look like in Haskell. It's no easier from the other direction!
Not sure I understand what was the question but yes, you are right, monad in Haskell is defined as a triple:
m :: * -> * -- this is endofunctor from haskell types to haskell types!
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
but common definition from category theory is another triple:
m :: * -> *
return :: a -> m a
join :: m (m a) -> m a
It is slightly confusing but it's not so hard to show that these two definitions are equal.
To do that we need to define join in terms of (>>=) (and vice versa).
First step:
join :: m (m a) -> m a
join x = ?
This gives us x :: m (m a).
All we can do with something that have type m _ is to aply (>>=) to it:
(x >>=) :: (m a -> m b) -> m b
Now we need something as a second argument for (>>=), and also,
from the type of join we have constraint (x >>= y) :: ma.
So y here will have type y :: ma -> ma and id :: a -> a fits it very well:
join x = x >>= id
The other way
(>>=) :: ma -> (a -> mb) -> m b
(>>=) x y = ?
Where x :: m a and y :: a -> m b.
To get m b from x and y we need something of type a.
Unfortunately, we can't extract a from m a. But we can substitute it for something else (remember, monad is a functor also):
fmap :: (a -> b) -> m a -> m b
fmap y x :: m (m b)
And it's perfectly fits as argument for join: (>>=) x y = join (fmap y x).
The best way to look at monads and computational effects is to start with where Haskell got the notion of monads for computational effects from, and then look at Haskell after you understand that. See this paper in particular: Notions of Computation and Monads, by E. Moggi.
See also Moggi's earlier paper which shows how monads work for the lambda calculus alone: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.26.2787
The fact that monads capture substitution, among other things (http://blog.sigfpe.com/2009/12/where-do-monads-come-from.html), and substitution is key to the lambda calculus, should give a good clue as to why they have so much expressive power.
While monads originally came from category theory, this doesn't mean that category theory is the only abstract context in which you can view them. A different viewpoint is given by operational semantics. For an introduction, have a look at my Operational Monad Tutorial.
One way to look at IO is to consider it as a strange kind of state monad. Remember that the state monad looks like:
data State s a = State (s -> (s, a))
where the "s" argument is the data type you want to thread through the computation. Also, this version of "State" doesn't have "get" and "put" actions and we don't export the constructor.
Now imagine a type
data RealWorld = RealWorld ......
This has no real definition, but notionally a value of type RealWorld holds the state of the entire universe outside the computer. Of course we can never have a value of type RealWorld, but you can imagine something like:
getChar :: RealWorld -> (RealWorld, Char)
In other words the "getChar" function takes a state of the universe before the keyboard button has been pressed, and returns the key pressed plus the state of the universe after the key has been pressed. Of course the problem is that the previous state of the world is still available to be referenced, which can't happen in reality.
But now we write this:
type IO = State RealWorld
getChar :: IO Char
Notionally, all we have done is wrap the previous version of "getChar" as a state action. But by doing this we can no longer access the "RealWorld" values because they are wrapped up inside the State monad.
So when a Haskell program wants to change a lightbulb it takes hold of the bulb and applies a "rotate" function to the RealWorld value inside IO.
For me, so far, the explanation that comes closest to tie together monads in category theory and monads in Haskell is that monads are a monid whose objects have the type a->m b. I can see that these objects are very close to an endofunctor and so the composition of such functions are related to an imperative sequence of program statements. Also functions which return IO functions are valid in pure functional code until the inner function is called from outside.
This id element is 'a -> m a' which fits in very well but the multiplication element is function composition which should be:
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
This is not quite function composition, but close enough (I think to get true function composition we need a complementary function which turns m b back into a, then we get function composition if we apply these in pairs?), I'm not quite sure how to get from that to this:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
I've got a feeling I may have seen an explanation of this in all the stuff that I read, without understanding its significance the first time through, so I will do some re-reading to try to (re)find an explanation of this.
The other thing I would like to do is link together all the different category theory explanations: endofunctor+2 natural transformations, Kleisli category, a monoid whose objects are monoids and so on. For me the thing that seems to link all these explanations is that they are two level. That is, normally we treat category objects as black-boxes where we imply their properties from their outside interactions, but here there seems to be a need to go one level inside the objects to see what’s going on? We can explain monads without this but only if we accept apparently arbitrary constructions.
Martin
See this question: is chaining operations the only thing that the monad class solves?
In it, I explain my vision that we must differentiate between the Monad class and individual types that solve individual problems. The Monad class, by itself, only solve the important problem of "chaining operations with choice" and mades this solution available to types being instance of it (by means of "inheritance").
On the other hand, if a given type that solves a given problem faces the problem of "chaining operations with choice" then, it should be made an instance (inherit) of the Monad class.
The fact is that problems not get solved merely by being a Monad. It would be like saying that "wheels" solve many problems, but actually "wheels" only solve a problem, and things with wheels solve many different problems.

Resources