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.