What are the steps to output this code without code?

I looked through some code and came across the following stone, which I put as a copy of pointfree output:

(I thought the following was more appropriate than the usual foo / bar for this particular question: P)

 import Control.Monad (liftM2) data Battleship = Battleship { x :: Int , y :: Int } deriving Show placeBattleship :: Int -> Int -> Battleship placeBattleship x' y' = Battleship { x = x', y = y' } coordinates :: Battleship -> (Int, Int) coordinates = liftM2 (,) xy 

Would someone be kind enough to explain the steps needed to simplify:

(i) coordinates b = (xb, yb)




(ii) coordinates = liftM2 (,) xy ?

In particular, I'm a little confused about using liftM2 , as I did not even know that the monad is hiding in the background.

I know that (i) can also be represented as: coordinates s = (,) (xs) (ys) , but I'm not sure where / how to act.


PS That's why I suspect it is from pointfree (output from GHCI and :pl with the alias pointfree ):

 Ξ»: :pl coordinates s = (xs, ys) coordinates = liftM2 (,) xy 
+6
source share
3 answers

This uses the Monad instance for (->) r , also called the "reader monad". It is a monad of functions from a certain type to a . (Look here for a motivation for why it exists in the first place.)

To see how it works for various functions, replace m with (r -> in ma . For example, if we just do liftM , we get:

 liftM :: (a -> b) -> (ma -> mb) liftM :: (a -> b) -> ((r -> a) -> (r -> b)) :: (a -> b) -> (r -> a) -> (r -> b) -- simplify parentheses 

... which is just a function of composition. Well maintained.

We can do the same for liftM2 :

 liftM2 :: (a -> b -> c) -> ma -> mb -> mc liftM2 :: (a -> b -> c) -> (r -> a) -> (r -> b) -> (r -> c) 

So, we see a way to create two one-parameter functions with a function of two arguments. This is a way to generalize the normal functional composition to several arguments. The idea is that we create a function that takes one r , passing it through both functions with one argument, getting two arguments to go into a function with two arguments. So, if we have f :: (r -> a) , g :: (r -> b) and h :: (a -> b -> c) , we get:

 \ r -> h (fr) (hr) 

Now, how does this relate to your code? (,) is a function with two arguments, and x and y are functions with one argument of type Battleship -> Int (because this is how field handlers work). With that in mind:

 liftM2 (,) xy = \ r -> (,) (xr) (yr) = \ r -> (xr, yr) 

Once you have learned the idea of ​​creating multiple functions in this way, dotted code like this will become pretty readable - no need to use the pointfree tool! In this case, I think that the non-pointfree version is still better, but hassle-free, not scary itself.

+9
source

The liftM2 monad works liftM2 , this is the function monad (->) a . This is equivalent to the Reader monad, as you could see before.

Recall the definition of liftM2 :

 liftM2 :: Monad m => (a -> b -> r) -> ma -> mb -> mr liftM2 f ma mb = do a <- ma b <- mb return $ fab 

So now, if we substitute (,) for f , x for ma and y for mb , we get

 liftM2 (,) xy = do a <- x b <- y return $ (,) ab 

Since x, y :: Battleship -> Int , which is equivalent to ((->) Battleship) Int , then m ~ (->) Battleship . The monad function is defined as

 instance Monad ((->) a) where return x = const x m >>= f = \a -> f (ma) a 

Essentially, what the monad function does is you can extract the result from several functions if they all have the same input. A more striking example might be something like

 test = do a <- (^2) b <- (^3) c <- (^4) d <- show return (a, b, c, d) > test 2 (4, 8, 16, "2") 
+5
source

You can easily rewrite

 data Battleship = Battleship { x :: Int , y :: Int } deriving Show placeBattleship :: Int -> Int -> Battleship placeBattleship xy = Battleship xy coordinates :: Battleship -> (Int, Int) coordinates (Battleship xy) = (x, y) 

This is not a dot style, but rather simple.

+1
source

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


All Articles