Is there any intuition to understand join two functions in Monad?

前提是你 提交于 2020-01-01 04:34:07

问题


join is defined along with bind to flatten the combined data structure into single structure.

From type system view, (+) 7 :: Num a => a -> a could be considered as a Functor, (+) :: Num a => a -> a -> a could be considered as a Functor of Functor, how to get some intuition about it instead of just relying on type system? Why join (+) 7 === 14?

Even though it is possible to get the final result through manually stepping by the function binding process, it would be great if some intuition were given.

This is from the NICTA exercises.

-- | Binds a function on the reader ((->) t).
--
-- >>> ((*) =<< (+10)) 7
-- 119
instance Bind ((->) t) where
  (=<<) ::
    (a -> ((->) t b))
    -> ((->) t a)
    -> ((->) t b)
  (f =<< a) t =
    f (a t) t

-- | Flattens a combined structure to a single structure.
--
-- >>> join (+) 7
-- 14
join ::
  Bind f =>
  f (f a)
  -> f a
join f =
  id =<< f

*Course.State> :t join (+)
join (+) :: Num a => a -> a
*Course.State> :t join
join :: Bind f => f (f a) -> f a
*Course.State> :t (+)
(+) :: Num a => a -> a -> a

回答1:


how to get some intuition about it instead of just relying on type system?

I'd rather say that relying on the type system is a great way to build a specific sort of intuition. The type of join is:

join :: Monad m => m (m a) -> m a

Specialised to (->) r, it becomes:

(r -> (r -> a)) -> (r -> a)

Now let's try to define join for functions:

-- join :: (r -> (r -> a)) -> (r -> a)
join f = -- etc.

We know the result must be a r -> a function:

join f = \x -> -- etc.

However, we do not know anything at all about what the r and a types are, and therefore we know nothing in particular about f :: r -> (r -> a) and x :: r. Our ignorance means there is literally just one thing we can do with them: passing x as an argument, both to f and to f x:

join f = \x -> f x x

Therefore, join for functions passes the same argument twice because that is the only possible implementation. Of course, that implementation is only a proper monadic join because it follows the monad laws:

join . fmap join = join . join
join . fmap return = id
join . return = id

Verifying that might be another nice exercise.




回答2:


Going along with the traditional analogy of a monad as a context for computation, join is a method of combining contexts. Let's start with your example. join (+) 7. Using a function as a monad implies the reader monad. (+ 1) is a reader monad which takes the environment and adds one to it. Thus, (+) would be a reader monad within a reader monad. The outer reader monad takes the environment n and returns a reader of the form (n +), which will take a new environment. join simply combines the two environments so that you provide it once and it applies the given parameter twice. join (+) === \x -> (+) x x.

Now, more in general, let's look at some other examples. The Maybe monad represents potential failure. A value of Nothing is a failed computation, whereas a Just x is a success. A Maybe within a Maybe is a computation that could fail twice. A value of Just (Just x) is obviously a success, so joining that produces Just x. A Nothing or a Just Nothing indicates failure at some point, so joining the possible failure should indicate that the computation failed, i.e. Nothing.

A similar analogy can be made for the list monad, for which join is merely concat, the writer monad, which uses the monoidal operator <> to combine the output values in question, or any other monad.

join is a fundamental property of monads and is the operation that makes it significantly stronger than a functor or an applicative functor. Functors can be mapped over, applicatives can be sequences, monads can be combined. Categorically, a monad is often defined as join and return. It just so happens that in Haskell we find it more convenient to define it in terms of return, (>>=), and fmap, but the two definitions have been proven synonymous.




回答3:


An intuition about join is that is squashes 2 containers into one. .e.g

join [[1]] => [1]
join (Just (Just 1)) => 1
join (a christmas tree decorated with small cristmas tree) => a cristmas tree

etc ...

Now, how can you join functions ? In fact functions, can be seen as a container. If you look at a Hash table for example. You give a key and you get a value (or not). It's a function key -> value (or if you prefer key -> Maybe value). So how would you join 2 HashMap ?

Let's say I have (in python style) h={"a": {"a": 1, "b": 2}, "b" : {"a" : 10, "b" : 20 }} how can I join it, or if you prefer flatten it ? Given "a" which value should I get ? h["a"] gives me {"a":1, "b":2}. The only thing I can do with it is to find "a" again in this new value, which gives me 1. Therefore join h equals to {"a":1, "b":20}.

It's the same for a function.



来源:https://stackoverflow.com/questions/32323680/is-there-any-intuition-to-understand-join-two-functions-in-monad

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!