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.