Monads and abstraction

I am new to haskell, functional language and monads.
I talked with this for about a month; I read haskell about you and played around with a peg trying to make my haskell site.

But something bothers me: the abstraction of monads. If I understand correctly, monads are “data containers” that can be ordered. I can “unzip” it using “→ =”, for example, and more will be done “behind the scenes” for me, so if I don’t have a monad definition, I have to guess how it will be unpacked.

For instance:

We have a list monad that unpacks it, will order its element

[1,2,3] >>= return . (+1) -- gives back [2,3,4] 

Or a more complex monad, like the writer in these examples: Log Writer Monad

Or I may have a webWriter monad, which for each "unpacking" of its values ​​sends a request to some remote server (I'm not sure about this, but I'm trying to give an extreme case)

My question is: can I say that the apply ('→ =', 'applyLog') function does behind the scenes only by looking at the monad's user interface (which, I think, is a type definition)?

I hope I explained myself well.

Thanks, Oren.

+6
source share
7 answers

While you can’t know what (>>=) does for a particular monad, just by looking at the interface, there are laws that each monad must follow in order to make the “right” monad. And this holds back the possible implementations of return and (>>=) . monad laws :

  • Left identity: return a >>= f equals fa
  • Identity Right: m >>= return equals m
  • Associativity: (m >>= f) >>= g is equal to m >>= (\x -> fx >>= g)

For example, if return for a list monad were defined as \x -> [x,x] instead of \x -> [x] , this would violate the law of left identity. return 5 >>= \x -> [x+1] will be different from (\x -> [x+1]) 5 .

In addition, not all monads can be intuitively understood as “containers” of some kind . The container analogy works for List and Maybe, but what about Reader, for example? The Reader value does not really “contain” anything. Instead, it is a description of a calculation that depends on an external, unchanging environment.

A monad is everything that implements the monad interface and respects the laws of the monad.

Edit: As an example of how to establish what a monad instance does for a given type, consider the Data.Stream.Infinite.Stream from the streams package. Streams are like lists, only they are always endless.

The stream has an instance of Monad. What will return and (>>=) do in this case?

return is of type a -> Stream a . The only possible function with this type is one that returns endless repetitions of the value passed as a parameter.

(>>=) more complicated. It is of type Stream a -> (a -> Stream b) -> Stream b . One possible function with this type is one that takes the beginning of the first argument and applies it to the second argument, returning the resulting stream. s >>= f = f $ head s .

Another possible implementation (>>=) would be to apply a function of type a -> Stream b to each element of the original stream, obtain an intermediate result of type Stream (Stream b) , and then somehow collapse the stream of streams into one Stream b value. How to do it? You can just take the diagonal of an infinite square!

Which version (>>=) compatible with the laws of the monad? The first, of course, is not, because it violates the correct identification. The result of 1,2,3,4... >>= return will be 1,1,1,1... The second implementation corresponds to the correct identifier (can you understand why?), And this gives us more confidence that this may be the correct implementation method (>>=) for threads. Of course, you will need factual evidence of all laws to be sure!

+8
source

I describe four sources of information that you can use to learn about >>= behavior for specific monads.

Type >>=

Type >>= always the same. It is specified in a class like Monad . See the documentation . A type:

 (>>=) :: forall a b. ma -> (a -> mb) -> mb 

Where m is a placeholder for the particular monad you are interested in. For example, for a list monad, type >>= :

 (>>=) :: forall a b. [a] -> (a -> [b]) -> [b] 

Note that I just replaced m ... with [...] .

Monad Laws

The implementation >>= is different for each monad, but all monads must adhere to the laws of the monad. These laws are indicated in the class documentation of the Monad type. See more details. The laws:

  • return a >>= k == ka
  • m >>= return == m
  • m >>= (\x -> kx >>= h) == (m >>= k) >>= h

So, whatever the implementation of any particular monad, you can use these laws to talk about your code. For example, if your code contains a code similar to the left side of the law, you can replace this code with the corresponding right side of the law, and the behavior should not change.

Here is an example of how to use the law of the monad. Suppose I wrote this code:

 foo = do x <- bar return x 

We don’t even know that a monad is used here, but we know that there is a monad, because we see the designation. To apply the monad law, we must drop the designation for calls >>= :

 foo = bar >>= (\x -> return x) 

Note that \x -> return x matches just return (by η-reduction.

 foo = bar >>= return 

According to the second law of the monad, this code means the same as the calling bar.

 foo = bar 

So it seems that >>= in the original function foo cannot do anything at all, because the laws of the monad allow us to just leave it. We understood this without even knowing which concrete monad the >>= operator provides here.

Specific Monad Documentation

If you need to learn more about the behavior >>= for a particular monad, you should report the documentation for that particular monad. You can use hoogle to search for documentation. For example, StateT documentation tells you:

The return function leaves the state unchanged, and >>= uses the final state of the first calculation as the initial state of the second.

The implementation of a specific monad

If you want to know more details about the implementation of a particular monad, you may have to look at the actual implementation. Find the instance Monad ... ad. For example, consider implementing StateT . The implementation of the list monad is somewhere in this file , look for instance Monad [] or look at this, except:

 instance Monad [] where m >>= k = foldr ((++) . k) [] m m >> k = foldr ((++) . (\ _ -> k)) [] m return x = [x] fail _ = [] 

This may not be the most obvious definition, but what exactly happens if you call >>= for the list monad.

Summary

All monads share type signatures for >>= and return and monad laws. Aparat of these restrictions, each monad provides a different implementation of >>= and return , and if you want to know all the details, you will have to study the source code of the instance Monad ... declaration. If you just want to learn how to use a specific monad, try finding documentation on this.

+4
source

A monad is not a "data container". A monad is a higher order computation structure. The value → = can be better understood if you consider <= <instead.

f . g f . g is a simple composition of functions

mf <=< mg - composition of the calculations.

In my opinion, this is more understandable. However, <= <can be defined via → =, so usually only → = needs to be defined. You can also define → = via <= <: m >>= f = (f <=< const m) ()

"ma" is not a data container. He only says that he implements "similar" behavior, so now we can only compose parts of the correct type together. (>>=) :: ma -> (a -> mb) -> mb tells us that since "ma" reveals "behavior", we can join a function that uses "a" -like behavior to turn into behavior of type "b".

How can he implement type a behavior for any type? So, why do we say that this is a functor: it maps any function a-> b to m a-> mb - for each function a-> b it finds (or builds) the function in such a way that if f and g are constellations, then mf and mg make up too. This is a strong statement about the conservation of the algebraic properties of types a and b: if f adds 1 and g selects 1, you get the same number; then the mf compiled with mg will also return you to where you started - it may not have a number stored anywhere, but the behavior will be "the same number."

Also, being a monad means that it only concerns a structure of a higher order and not the actual type: since "ma" can have any type a, this means that the implementation cannot depend on the specifics of this type. A monad can only use the algebraic structure of computation - in the broad sense of the word "algebraic". For example, a list, [a], can have any elements, but the monad can only work with the algebraic structure of the list - list the elements, split the list, foldr, etc., but cannot, for example, add all the elements - adding "looks like" . Other monads will also have certain monads; without them, they can be completely useless - for example, ask , atomically , retry , etc.

+3
source

Looking only at the monad API type signature, it looks the same as the function type signature, for example: a -> b -> c , which only tells you that, given some things, the function can give you something yet. How a function does this is a granularity of the function.

In the same way, bind , return and other special functions of a monad (for example, put , get in a state monad) give only the idea that all comparisons from one thing to another can be done using these functions. If you need to understand the basic logic of how , in fact, the monad works, you either look at the documentation (if any) or the source code.

+2
source

May I say that the function apply ... does behind the scenes

There are many perfectly accurate answers to a question that you did not ask. Can you tell what the monad does from the context of its use? No, not in general.

You should hope that the name of the monad and its documentation (often very rare, I'm afraid) will help, and that the surrounding code (often very short) provides you with context.

Of course, if this is your code or code base that you are familiar with, then it is simple. However, most Haskell code does not seem to be optimized for being “skimmable”.

+2
source

This is an interesting question. I would say that you have a decent understanding of what monads can do. I would say that it is impossible to find out the specific behavior of any function without reading the documentation. Each monad binding and return is implemented specifically for the type structure and purpose.

+1
source

You cannot say for sure what return or >>= will do for a given monad, looking exclusively at their type signatures. This is because their type signatures will always be

 return :: Monad m => a -> ma (>>=) :: Monad m => ma -> (a -> mb) -> mb 

Très generic. Which is excellent at the type level, as this is the exact specification of the functions there.

With finer resolution, you will need to view the declaration of the monad instance.

+1
source

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


All Articles