the Cont r a type stands for a function which takes a continuation a->r and produces a result of type r. So both the continuation and the entire Cont r a produce a result of the same type r.
My question is: are the two results necessarily the same value, or can a Cont r a post-process the result from the continuation and produce a different value, albeit of the same type r?
I tried using (+1) for post-processing (note the + 1 --<--):
c1 :: Int -> Cont r Int
c1 x = let y = 2*x
in cont $ \k -> (k y) + 1 --<--
Now that doesn't typecheck, because my post-processing function (+1) only accepts an argument whose type belongs to the Num typeclass. However, I pass the result of the continuation (k y) which is of some type r that is not guaranteed to belong to the Num typeclass.
Whatever I do to (k y), it must be a function of type r->r. The only function which can do this for all r is the id function and using id for post-processing is no post-processing at all.
However, the whole thing does typecheck if I restrict r to the Num typeclass or even to the concrete type Int. It then produces the expected result:
*Main> runCont (c1 1) id
3
I am quite unsure,
if such post-processing and restricting the type of r is a normal thing to do, and if so, in what circumstances this might be useful
or if the type variable rhas to be read as for all r and restricting the type of r will lead to all sorts of trouble.
Can someone shed some light on this?
Technically, I think it's fine. Specializing Cont r a to Num r => Cont r a doesn't seem fundamentally more problematic than specializing Reader r a to Num r => Reader r a.
An implication of doing so is that the resulting CPS computation can only be run against a (final) continuation that produces a number, but that's obvious -- if you have a computation that post-processes the continuation result as a number, it can only be used with continuations that produce numbers!
As additional evidence that this is sanctioned at least to some degree, note that there's a function:
mapCont :: (r -> r) -> Cont r a -> Cont r a
If this function was to be used with no restriction on r, the only valid values for its first argument would be id or functions that don't terminate, as you have noted.
A version of your c1 using mapCont might look like:
c2 :: (Num r) => Int -> Cont r Int
c2 x = mapCont (+1) $ return (2*x)
and seems to work fine:
> runCont (c2 10) id
21
> runCont (c2 10) (const 5)
6
> runCont (c2 10) show
... No instance for (Num String) arising from a use of 'c2' ...
As for when this would be useful, I'm not sure. I can think of a few somewhat lame applications. You could define an computation that overrides the final result (provided no other kind of post-processing is used):
override x = cont (const x)
to be used like:
> runCont (return 2 >>= \x -> cont (\f -> f (x*3))) id
6
> runCont (return 2 >> override 1000 >>= \x -> cont (\f -> f (x*3))) id
1000
>
or a computation transformer that emulates a writer to add log functionality:
annotate note comp = mapCont (\(a, w) -> (a, note:w)) comp
which you might use like this:
runCont (annotate "two" (return 2)
>>= \x -> annotate "times three" (cont (\f -> f (x*3))))
(\a -> (a, []))
yielding:
(6,["two","times three"])
These don't seem like very compelling applications, though.
#KABuhr has shown that post-processing in the ordinary Cont works, but didn't find "very compelling applications". I'm going to show you how post-processing is useful, but it only works best when you generalize Cont. First, some header stuff (mostly used in the examples):
{-# LANGUAGE RebindableSyntax #-}
import Prelude(Num(..), Eq(..), Enum(..))
import Data.Bool
import Data.Function
import Data.Functor.Identity
import Data.List
import Data.Maybe
import Data.Tuple
import Control.Lens(_1, _2, traversed)
Now, a generalized Cont.
newtype Cont r f a = Cont { runCont :: (a -> r) -> f }
Your question was "is post-processing allowed in Cont?" The answer is yes. If would like it to not be so, you can use newtype ContS a = { runContS :: forall r. (a -> r) -> r } which totally disallows it. In fact, ContS a is isomorphic to a. The Cont I just defined takes the opposite position: even type-changing post-processors are allowed. We can define a standard Functorial (<$>).
infixl 1 <$>
(<$>) :: (a -> b) -> Cont r f a -> Cont r f b
f <$> Cont x = Cont $ \cont -> x $ \realX -> cont (f realX)
Before continuing, let's get an understanding of the metaphor behind Cont. A Cont r f a is a computation that can produce as. It will give you the as, but will ask you to produce rs. Once you do that, it'll make fs. It's sort of like a (r -> f, a), but with heavy restrictions on use. If we try to define an Applicative-ish operator, we see something interesting.
infixl 1 <*>
(<*>) :: Cont m f (a -> b) -> Cont r m a -> Cont r f b
Cont f <*> Cont x = Cont $ \cont -> x $ \realX -> f $ \realF -> cont (realF realX)
(<*>) is sort of doing two operations at once. It is applying the a -> b to an a to get b, but it's also composing the m -> f and r -> m aspects into a r -> f part. However, the type of (<*>) no longer fits into the normal Applicative format. This is why we use Cont r a instead of Cont r f a. The former is less powerful, but it fits into our existing framework. To get our Cont to work, we have to leave some of the established infrastructure behind.
Before we get into the RebindableSyntax-level stuff, here's some usage.
complete :: Cont a f a -> f
complete (Cont x) = x id
amb :: [a] -> Cont (Maybe b) (Maybe (a, b)) a
amb [] = Cont (const Nothing)
amb (x : xs) = Cont $ \test -> case test x of
Nothing -> runCont (amb xs) test
Just y -> Just (x, y)
poly :: Num a => a -> a -> a -> a
poly x y z = sq x * y + sq y + z + sq z * x
where sq x = x * x
solution :: (Num a, Enum a, Eq a) => Maybe (a, (a, (a, ())))
solution = complete $ testRoot <$> amb [-5..5]
<*> amb [-10 .. -5]
<*> amb [5..10]
where testRoot x y z = case poly x y z of
0 -> Just ()
_ -> Nothing
complete completes a computation when there isn't actually a gap holding it up. amb takes a [a], and goes through each a, one by one. It passes each into the test, and searches until it finds one that succeeds. It post-processes the result of the test in two ways. It resets the result until it's a Just (or gives up), and a Just result gets up paired with the input that built it.
In solution, the complete is delimiting the extent of the continuation passed to the ambs. Each amb is passed the code that lies between it and the complete. E.g., the continuation given to the amb [-5..5] is \x -> testRoot x <*> amb [-10 .. -5] <*> amb [10..5]. This style of continuations is called shift/reset. Cont is shift, complete is reset. The idea is that amb [-5..5] is a "liar"; it "looks like" a Num a => a because it's getting passed to testRoot, but it's actually a control structure that turns everything around it inside-out. Compared to the normal Cont r a, the control structures allowed in our Cont are more powerful.
Now, here's what we need RebindableSyntax for:
(=<<) :: (a -> Cont r m b) -> Cont m f a -> Cont r f b
f =<< Cont x = Cont $ \cont -> x $ \realX -> runCont (f realX) cont
(>>=) = flip (=<<)
return :: a -> Cont r r a
return x = Cont ($ x)
(=<<) is the Monad-style function application operator. Again, our version doesn't fit the usual type. With (>>=) and return, do-notation has now been redefined to work with Cont. You can go back and rewrite solution in do-notation to see that it works.
Let's really get out there. The idea behind profunctor optics is that data structures give rise to "transformer transformers". E.g. a Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t takes a transformer between the "small" structures a and b and makes one from between the "bigger" s and t. Look what lies just a flip away...
editing :: ((a -> Identity b) -> s -> Identity t) -> s -> Cont b t a
editing optic x = Cont (runIdentity . flip optic x . (Identity .))
editing, as a control structure, takes a reference to a field inside a structure, a structure to use it on, and then mutates that structure with "the rest of the program." Using it, you can write the following:
example :: (a -> a) -> [(Bool, (a, a))] -> [(Bool, (a, a))]
example f xs = complete $ do x <- editing traversed xs
n2 <- editing _2 x
n <- case fst x of
True -> editing _1 n2
False -> editing _2 n2
return (f n)
I hope, with even these contrived examples, that you're convinced that post-processing is useful in Cont. There's nothing wrong with doing it. However, if you want to use it at its full potential, you have to break out of the existing Applicative and Monad form. This is painful, so we cripple Cont to make it fit, disabling type-changing post-processing as a trade-off.
Related
I'm trying to create an instance for bind operator (>>=) to the custom type ST a
I found this way to do it but I don't like that hardcoded 0.
Is there any way to implement it without having the hardcoded 0 and respecting the type of the function?
newtype ST a = S (Int -> (a, Int))
-- This may be useful to implement ">>=" (bind), but it is not mandatory to use it
runState :: ST a -> Int -> (a, Int)
runState (S s) = s
instance Monad ST where
return :: a -> ST a
return x = S (\n -> (x, n))
(>>=) :: ST a -> (a -> ST b) -> ST b
s >>= f = f (fst (runState s 0))
I often find it easier to follow such code with a certain type of a pseudocode rewrite, like this: starting with the
instance Monad ST where
return :: a -> ST a
return x = S (\n -> (x, n))
we get to the
runState (return x) n = (x, n)
which expresses the same thing exactly. It is now a kind of a definition through an interaction law that it must follow. This allows me to ignore the "noise"/wrapping around the essential stuff.
Similarly, then, we have
(>>=) :: ST a -> (a -> ST b) -> ST b
s >>= f = -- f (fst (runState s 0)) -- nah, 0? what's that?
--
-- runState (s >>= f) n = runState (f a) i where
-- (a, i) = runState s n
--
S $ \ n -> let (a, i) = runState s n in
runState (f a) i
because now we have an Int in sight (i.e. in scope), n, that will get provided to us when the combined computation s >>= f will "run". I mean, when it will runState.
Of course nothing actually runs until called upon from main. But it can be a helpful metaphor to hold in mind.
The way we've defined it is both the easiest and the most general, which is usually the way to go. There are more ways to make the types fit though.
One is to use n twice, in the input to the second runState as well, but this will leave the i hanging unused.
Another way is to flip the time arrow around w.r.t. the state passing, with
S $ \ n -> let (a, i2) = runState s i
(b, i ) = runState (f a) n
in (b, i2)
which is a bit weird to say the least. s still runs first (as expected for the s >>= f combination) to produce the value a from which f creates the second computation stage, but the state is being passed around in the opposite direction.
The most important thing to keep in mind is that your ST type is a wrapper around a function. What if you started your definition as (>>=) = \s -> \f -> S (\n -> ... )? It might be (ok, is) a bit silly to write separate lambdas for the s and f parameters there, but I did it to show that they're not really any different from the n parameter. You can use it in your definition of (>>=).
the Cont r a type stands for a function which takes a continuation a->r and produces a result of type r. So both the continuation and the entire Cont r a produce a result of the same type r.
My question is: are the two results necessarily the same value, or can a Cont r a post-process the result from the continuation and produce a different value, albeit of the same type r?
I tried using (+1) for post-processing (note the + 1 --<--):
c1 :: Int -> Cont r Int
c1 x = let y = 2*x
in cont $ \k -> (k y) + 1 --<--
Now that doesn't typecheck, because my post-processing function (+1) only accepts an argument whose type belongs to the Num typeclass. However, I pass the result of the continuation (k y) which is of some type r that is not guaranteed to belong to the Num typeclass.
Whatever I do to (k y), it must be a function of type r->r. The only function which can do this for all r is the id function and using id for post-processing is no post-processing at all.
However, the whole thing does typecheck if I restrict r to the Num typeclass or even to the concrete type Int. It then produces the expected result:
*Main> runCont (c1 1) id
3
I am quite unsure,
if such post-processing and restricting the type of r is a normal thing to do, and if so, in what circumstances this might be useful
or if the type variable rhas to be read as for all r and restricting the type of r will lead to all sorts of trouble.
Can someone shed some light on this?
Technically, I think it's fine. Specializing Cont r a to Num r => Cont r a doesn't seem fundamentally more problematic than specializing Reader r a to Num r => Reader r a.
An implication of doing so is that the resulting CPS computation can only be run against a (final) continuation that produces a number, but that's obvious -- if you have a computation that post-processes the continuation result as a number, it can only be used with continuations that produce numbers!
As additional evidence that this is sanctioned at least to some degree, note that there's a function:
mapCont :: (r -> r) -> Cont r a -> Cont r a
If this function was to be used with no restriction on r, the only valid values for its first argument would be id or functions that don't terminate, as you have noted.
A version of your c1 using mapCont might look like:
c2 :: (Num r) => Int -> Cont r Int
c2 x = mapCont (+1) $ return (2*x)
and seems to work fine:
> runCont (c2 10) id
21
> runCont (c2 10) (const 5)
6
> runCont (c2 10) show
... No instance for (Num String) arising from a use of 'c2' ...
As for when this would be useful, I'm not sure. I can think of a few somewhat lame applications. You could define an computation that overrides the final result (provided no other kind of post-processing is used):
override x = cont (const x)
to be used like:
> runCont (return 2 >>= \x -> cont (\f -> f (x*3))) id
6
> runCont (return 2 >> override 1000 >>= \x -> cont (\f -> f (x*3))) id
1000
>
or a computation transformer that emulates a writer to add log functionality:
annotate note comp = mapCont (\(a, w) -> (a, note:w)) comp
which you might use like this:
runCont (annotate "two" (return 2)
>>= \x -> annotate "times three" (cont (\f -> f (x*3))))
(\a -> (a, []))
yielding:
(6,["two","times three"])
These don't seem like very compelling applications, though.
#KABuhr has shown that post-processing in the ordinary Cont works, but didn't find "very compelling applications". I'm going to show you how post-processing is useful, but it only works best when you generalize Cont. First, some header stuff (mostly used in the examples):
{-# LANGUAGE RebindableSyntax #-}
import Prelude(Num(..), Eq(..), Enum(..))
import Data.Bool
import Data.Function
import Data.Functor.Identity
import Data.List
import Data.Maybe
import Data.Tuple
import Control.Lens(_1, _2, traversed)
Now, a generalized Cont.
newtype Cont r f a = Cont { runCont :: (a -> r) -> f }
Your question was "is post-processing allowed in Cont?" The answer is yes. If would like it to not be so, you can use newtype ContS a = { runContS :: forall r. (a -> r) -> r } which totally disallows it. In fact, ContS a is isomorphic to a. The Cont I just defined takes the opposite position: even type-changing post-processors are allowed. We can define a standard Functorial (<$>).
infixl 1 <$>
(<$>) :: (a -> b) -> Cont r f a -> Cont r f b
f <$> Cont x = Cont $ \cont -> x $ \realX -> cont (f realX)
Before continuing, let's get an understanding of the metaphor behind Cont. A Cont r f a is a computation that can produce as. It will give you the as, but will ask you to produce rs. Once you do that, it'll make fs. It's sort of like a (r -> f, a), but with heavy restrictions on use. If we try to define an Applicative-ish operator, we see something interesting.
infixl 1 <*>
(<*>) :: Cont m f (a -> b) -> Cont r m a -> Cont r f b
Cont f <*> Cont x = Cont $ \cont -> x $ \realX -> f $ \realF -> cont (realF realX)
(<*>) is sort of doing two operations at once. It is applying the a -> b to an a to get b, but it's also composing the m -> f and r -> m aspects into a r -> f part. However, the type of (<*>) no longer fits into the normal Applicative format. This is why we use Cont r a instead of Cont r f a. The former is less powerful, but it fits into our existing framework. To get our Cont to work, we have to leave some of the established infrastructure behind.
Before we get into the RebindableSyntax-level stuff, here's some usage.
complete :: Cont a f a -> f
complete (Cont x) = x id
amb :: [a] -> Cont (Maybe b) (Maybe (a, b)) a
amb [] = Cont (const Nothing)
amb (x : xs) = Cont $ \test -> case test x of
Nothing -> runCont (amb xs) test
Just y -> Just (x, y)
poly :: Num a => a -> a -> a -> a
poly x y z = sq x * y + sq y + z + sq z * x
where sq x = x * x
solution :: (Num a, Enum a, Eq a) => Maybe (a, (a, (a, ())))
solution = complete $ testRoot <$> amb [-5..5]
<*> amb [-10 .. -5]
<*> amb [5..10]
where testRoot x y z = case poly x y z of
0 -> Just ()
_ -> Nothing
complete completes a computation when there isn't actually a gap holding it up. amb takes a [a], and goes through each a, one by one. It passes each into the test, and searches until it finds one that succeeds. It post-processes the result of the test in two ways. It resets the result until it's a Just (or gives up), and a Just result gets up paired with the input that built it.
In solution, the complete is delimiting the extent of the continuation passed to the ambs. Each amb is passed the code that lies between it and the complete. E.g., the continuation given to the amb [-5..5] is \x -> testRoot x <*> amb [-10 .. -5] <*> amb [10..5]. This style of continuations is called shift/reset. Cont is shift, complete is reset. The idea is that amb [-5..5] is a "liar"; it "looks like" a Num a => a because it's getting passed to testRoot, but it's actually a control structure that turns everything around it inside-out. Compared to the normal Cont r a, the control structures allowed in our Cont are more powerful.
Now, here's what we need RebindableSyntax for:
(=<<) :: (a -> Cont r m b) -> Cont m f a -> Cont r f b
f =<< Cont x = Cont $ \cont -> x $ \realX -> runCont (f realX) cont
(>>=) = flip (=<<)
return :: a -> Cont r r a
return x = Cont ($ x)
(=<<) is the Monad-style function application operator. Again, our version doesn't fit the usual type. With (>>=) and return, do-notation has now been redefined to work with Cont. You can go back and rewrite solution in do-notation to see that it works.
Let's really get out there. The idea behind profunctor optics is that data structures give rise to "transformer transformers". E.g. a Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t takes a transformer between the "small" structures a and b and makes one from between the "bigger" s and t. Look what lies just a flip away...
editing :: ((a -> Identity b) -> s -> Identity t) -> s -> Cont b t a
editing optic x = Cont (runIdentity . flip optic x . (Identity .))
editing, as a control structure, takes a reference to a field inside a structure, a structure to use it on, and then mutates that structure with "the rest of the program." Using it, you can write the following:
example :: (a -> a) -> [(Bool, (a, a))] -> [(Bool, (a, a))]
example f xs = complete $ do x <- editing traversed xs
n2 <- editing _2 x
n <- case fst x of
True -> editing _1 n2
False -> editing _2 n2
return (f n)
I hope, with even these contrived examples, that you're convinced that post-processing is useful in Cont. There's nothing wrong with doing it. However, if you want to use it at its full potential, you have to break out of the existing Applicative and Monad form. This is painful, so we cripple Cont to make it fit, disabling type-changing post-processing as a trade-off.
I have come across several situations where I would like to use applicative style f <$> x1 <*> x2 <*> x3 but scanning the applicative arguments right to left instead of the usual left to right.
Naturally, if I bring this into a monadic context, I can do this without problem:
liftM3' :: Monad m => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
liftM3' f x1 x2 x3 = do { x3' <- x3; x2' <- x2; x1' <- x1; return f x1' x2' x3' }
So, for my question: is there some general method to accomplish this in the context of only Applicative (perhaps a newtype wrapper) and if not, why can there not be one. That said, any insight about elegant solutions or workarounds to this problem are welcome.
Aside: My solution had been to define new right associative operators, but the solution was by no means elegant.
Edit: Here is my solution (I'd be interested in knowing if there is something equivalent in the standard libraries), if I require Monad:
newtype Reverse m a = Reverse (m a)
instance Monad m => Functor (Reverse m) where
f `fmap` x = pure f <*> x
instance Monad m => Applicative (Reverse m) where
pure x = Reverse $ return x
(Reverse f) <*> (Reverse x) = Reverse $ do { x' <- x; f' <- f; return $ f' x' }
The Backwards type is like your Reverse, and in a semi-standard package.
Naturally, if I bring this into a monadic context, I can do this without problem:
Don't forget that f is just a function. As such, you can simply define another function that takes the arguments in another order and then fall back to the usual applicative combinators:
-- | Lifts the given function into an applicative context.
-- The applicative effects are handled from right-to-left
-- e.g.
-- >>> liftA3 (\_ _ _ -> ()) (putStr "a") (putStr "b") (putStr "c")
-- will put "cba" on your console.
liftA3Rev :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3Rev f x y z = f' <$> z <*> y <*> x
where
f' = \c b a -> f a b c
It's probably either impossible, or quite hard to write this with operators only, though. This is due to the nature of partial application. Remember that for f :: Int -> Char -> Bool and Applicative f => f Int, the expression f <$> x
has type Applicative f => f (Char -> Bool). We always "lose" types on the left end, not on the right end. If you change the order of arguments, it's easy again:
(>*>) :: Applicative f => f a -> f (a -> b) -> f b
(>*>) = flip (<*>)
infixr 4 >*> -- right associative
liftA3Rev' :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3Rev' f x y z = z >*> y >*> x >*> pure f
Suppose we have a monad, defined by return, (>>=) and the set of laws. There is a data type
newtype C m a = C { unC ∷ forall r. (a → m r) → m r }
also known as Codensity. C m a ≅ m a given that m is a Monad, i.e. we can write two functions to ∷ Monad m ⇒ m a → C m a and from ∷ Monad m ⇒ C m a → m a
to ∷ Monad m ⇒ m a → C m a
to t = C $ \f → t >>= f
from ∷ Monad m ⇒ C m a → m a
from = ($ return) . unC
and show that to ∘ from ≡ id and from ∘ to ≡ id by equational reasoning, for example:
from . to = -- by definition of `(.)'
\x → from (to x) = -- by definition of `to'
\x → from (C $ \f → x >>= f) = -- by definition of `from'
\x → ($ return) (unC (C $ \f → x >>= f)) = -- unC . C ≡ id
\x → ($ return) (\f → x >>= f) = -- β-reduce
\x → x >>= return = -- right identity law
\x → x = -- by definition of `id'
id
So far so good. My questions are
Given a type and a bunch of laws, how do we construct corresponding isomorphic CPS representation?
Is this representation unique (I'd guess no)?
If it's not unique, is there always the most "simple" (in the number of →s for example :) ) one?
No such encoding is Unique
As you posed the question the answer is obviously "no"
a = forall r. (a -> r) -> r
a = forall s. ((forall r. (a -> r) -> r) -> s) -> s
as to which encoding is the smallest...well the underlying type is almost certainly!
Codensity might not be what you want
whats more, although Codensity is fascinating, I don't believe it is isomorphic to the underlying type. from . to = id is the easy direction.
to . from
= \x -> to (from x)
= \x -> C $ \f -> (from x) >>= f
= \x -> C $ \f -> (unC x return) >>= f
= \(C g) -> C $ \f -> (g return) >>= f
but then you get kinda stuck. The same thing happens when you try to prove a = forall r. (a -> r) -> r but you get saved by the "theorem for free" (there might be a way to do it without this, but the free theorem makes it easy). I know of no corresponding argument for Codensity, and what most papers I have read prove is rather that it preserves >>= and return, that is, if you only construct your C m a using the monadic operations and what you call to then the call to to . from is identity.
If we try hard enough we can even come up with a counter example to the isomorphism
evil :: C Maybe Int
evil = C $ \h -> case h 1 of
Nothing -> h 2
Just x -> Nothing
to . from $ evil
= (\(C g) -> C $ \f -> (g return) >>= f) evil
= C $ \f -> ((\h -> case h 1 of
Nothing -> h 2
Just x -> Nothing) return) >>= f
= C $ \f -> Nothing >>= f
so are these the same?
test 1 = Nothing
test n = Just n
unC evil test
= Just 2
unC (C $ \f -> Nothing >>= f) test
= Nothing >>= test
= Nothing
I might have made a mistake in that derivation. I have not really checked it, but suffice it to say that right now I don't think C m a = m a
Another Kind of CPS
All data can be encoded as untyped lambda functions, a property discovered by Church about 70 years ago. Often we talk about "Church encoding" data structures, although Oleg has suggested that instead, at least in a typed setting, we should talk about "Boehm-Beraducci" encodings. Regardless of what you call it, the idea is
(a,b) = forall r. (a -> b -> r) -> r
Either a b = forall r. (a -> r) -> (b -> r) -> r
at least up to fast and loose reasoning. It should be obvious that this encoding provides a way to encode any ADT as a System F type. This also reveals a way of implementing functional languages: treat everything as closures under the hood, and have pattern matching just be implemented as function application.
Actually, System F even has a way of encoding existential types as universal types
exists a. f a = forall r. (forall a'. f a' -> r) -> r
which turns out to be a very important identity. Among other things, this helps us to think about the relation between type inference for higher rank types and for existential types. Since type inference is decidable up to rank 2 types, type inference is also decidable in a system with rank 1 universals and existentials. Since existential quantification is the basis for modules, this is important stuff.
(m >>= f) >>= g = m >>= (\x -> f x >>= g)
what's different from f and \x->f x ??
I think they're the same type a -> m b. but it seems that the second >>= at right side of equation treats the type of \x->f x as m b.
what's going wrong?
The expressions f and \x -> f x do, for most purposes, mean the same thing. However, the scope of a lambda expression extends as far to the right as possible, i.e. m >>= (\x -> (f x >>= g)).
If the types are m :: m a, f :: a -> m b, and g :: b -> m c, then on the left we have (m >>= f) :: m b, and on the right we have (\x -> f x >>= g) :: a -> m c.
So, the difference between the two expressions is just which order the (>>=) operations are performed, much like the expressions 1 + (2 + 3) and (1 + 2) + 3 differ only in the order in which the additions are performed.
The monad laws require that, like addition, the answer should be the same for both.