The join
utility function is defined as:
join :: (Monad m) => m (m a) -> m a
join x = x >>= id
Given that the type of >>=
is Monad m => m a -> (a -> m b) -> m b
and id
is a -> a
, how can that function also be typed as a -> m b
as it must be in the definition above? What are m
and b
in this case?
in goes along with let to name one or more local expressions in a pure function. would print "hello world". And you just need one in before you use the names you just defined. And these names definitely don't have to be used as parameters to a function -- they can be just about anything, even functions themselves!
One is of type (String,Int) , whereas the other is (Int,String) . This has implications for building up lists of tuples. We could very well have lists like [("a",1),("b",9),("c",9)] , but Haskell cannot have a list like [("a",1),(2,"b"),(9,"c")] .
(:==) is not a valid symbol for a function or variable identifier in Haskell.
If you need to figure out what the type of an object is in a Haskell program, I hope this is helpful. Note that if you are in GHCI, you can just put :type before your expression to determine the expression's type, or use :set +t to see the type of every expression in GHCI.
The a
s in the types for >>=
and id
aren't necessarily the same a
s, so let's restate the types like this:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
id :: c -> c
So we can conclude that c
is the same as a
after all, at least when id
is the second argument to >>=
... and also that c
is the same as m b
. So a
is the same as m b
. In other words:
(>>= id) :: Monad m => m (m b) -> m b
dave4420 hits it, but I think the following remarks might still be useful.
There are rules that you can use to validly "rewrite" a type into another type that's compatible with the original. These rules involve replacing all occurrences of a type variable with some other type:
id :: a -> a
, you can replace a
with c
and get id :: c -> c
. This latter type can also be rewritten to the original id :: a -> a
, which means that these two types are equivalent. As a general rule, if you replace all instances of type variable with another type variable that occurs nowhere in the original, you get an equivalent type.id :: a -> a
, you can rewrite that to id :: Int -> Int
. The latter however can't be rewritten back to the original, so in this case you're specializing the type.f :: a -> m b
, you can replace all occurrences of a
with m b
and get f :: m b -> m b
. Since this one can't be undone either, it's also a specialization.That last example shows how id
can be used as the second argument of >>=
. So the answer to your question is that we can rewrite and derive types as follows:
1. (>>=) :: m a -> (a -> m b) -> m b (premise)
2. id :: a -> a (premise)
3. (>>=) :: m (m b) -> (m b -> m b) -> m b (replace a with m b in #1)
4. id :: m b -> m b (replace a with m b in #2)
.
.
.
n. (>>= id) :: m (m b) -> m b (indirectly from #3 and #4)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With