Is monad expressiveness due to code reuse?

When I compare binary operations of Applicative and Monad

(<*>) :: Applicative f => f (a -> b) -> fa -> fb (=<<) :: Monad m => (a -> mb) -> ma -> mb 

two differences become apparent:

  • ap expects a normal pure function, while bind expects a monadic action that should return a monad of the same type
  • with ap sequence of actions is determined by applicative, whereas with bind monadic action can determine the control flow

So, monads give me extra expressive power. However, since they no longer accept normal, pure functions, this expressiveness appears to be due to code reuse.

My conclusion may be somewhat naive or even false, since I have little experience with Haskell and monadic calculations. Any light in the dark is appreciated!

+5
source share
3 answers

Reusing code is only an advantage if you can reuse code to do what you really want.

 GHCi> putStrLn =<< getLine Sulfur Sulfur GHCi> 

Here (=<<) selects the String result created in the IO context using getLine and passes it to putStrLn , which then prints the specified result.

 GHCi> :t getLine getLine :: IO String GHCi> :t putStrLn putStrLn :: String -> IO () GHCi> :t putStrLn =<< getLine putStrLn =<< getLine :: IO () 

Now, in type fmap / (<$>) ...

 GHCi> :t (<$>) (<$>) :: Functor f => (a -> b) -> fa -> fb 

... it is entirely possible that b will be IO () , and therefore nothing prevents us from using putStrLn with it. But...

 GHCi> putStrLn <$> getLine Sulfur GHCi> 

... nothing will be printed.

 GHCi> :t putStrLn <$> getLine putStrLn <$> getLine :: IO (IO ()) 

Performing an IO (IO ()) action will not perform an internal IO () action. To do this, we need additional Monad power by replacing (<$>) with (=<<) or, which is the same, using join by the value of IO (IO ()) :

 GHCi> :t join join :: Monad m => m (ma) -> ma GHCi> join (putStrLn <$> getLine) Sulfur Sulfur GHCi> 

Like me, it’s also difficult for me to understand the background of your question. You seem to expect one of Functor , Applicative and Monad be better than the rest. This is not relevant. We can do more with Applicative than with Functor , and even more with Monad . If you need extra power, use the appropriate class. If not, using a less powerful class will result in simpler, more understandable code and a wider range of available instances.

+3
source

If you have a pure function g :: a -> b , you can make it a Applicative version on

pure g :: Applicative f => f (a -> b)

or Monad ish on

pure . g :: Applicative f => a -> fb

Thus, you will not lose the reuse of code in your understanding.

+8
source

The reason for this question was my too schematic understanding of the relationship between functor, applicative and monadic class. I thought it was only about reusing pure functions.

(<*>) essentially says that they attribute to me functions and a bunch of applied values, and I will apply them in accordance with my rules.

(=<<) essentially says that it gives me the function (a -> mb) and the monad, and I will feed (a -> mb) value of the monad and leave it (a -> mb) to create and return the converted value, wrapped in the same monad.

Of course, the applicative code will be shorter and better repetitive, because the mechanism of how the sequence of actions is performed is determined exclusively inside (<*>) . However, applicative sequences are also mechanical. Therefore, when you want to control the flow of a sequence or the "shape" of the resulting structure, you need extra monad power, which leads to more detailed code.

I think this question is not particularly useful, and if people vote for the closure, I will have no problem removing it.

+1
source

Source: https://habr.com/ru/post/1259681/


All Articles