Applicative permutation to generate knight moves I want to understand Haskell better by trying to stretch possibilities of Functor, Applicative, and Monad as much as possible and study how they behave. So in Learn You a Haskell there's an exercise to generate all possible moves for a knight from a given position on chessboard:


moveKnight :: KnightPos -&gt; [KnightPos]  
moveKnight (c,r) = filter onBoard  
    [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)  
    ,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)  
    ]  
    where onBoard (c,r) = c `elem` [1..8] &amp;&amp; r `elem` [1..8]



Now I thought to myself, wait a second, I can actually generate that list [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1),(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)] instead of hardcoding it. So this is how I did it with list comprehensions:

invert x
  | x == 1 = 2
  | x == 2 = 1
  | otherwise = 0

moves = [(c `f` a, r `g` (invert a)) |
         c &lt;- [6],
         r &lt;- [2],
         f &lt;- [(+), (-)],
         g &lt;- [(+), (-)],
         a &lt;- [1, 2]]

-- generates: [(7,4),(8,3),(7,0),(8,1),(5,4),(4,3),(5,0),(4,1)]


So the above works correctly, but the code looks ugly. Because every list comprehension is equivalent to a lift, the above code could be rewritten with liftA5:

liftA5 :: Applicative g =&gt; (a -&gt; b -&gt; c -&gt; d -&gt; e -&gt; f) -&gt; g a -&gt; g b -&gt; g c -&gt; g d -&gt; g e -&gt; g f
liftA5 f a b c d e = f &lt;$&gt; a &lt;*&gt; b &lt;*&gt; c &lt;*&gt; d &lt;*&gt; e

liftA5 (\c r f g a -&gt; (c `f` a, r `g` (invert a))) [6] [2] [(+), (-)] [(+), (-)] [1,2]


My questions are:


Are there any way to generate the above list in a more elegant way?
How to generalize it to arbitrary possible permutations? (So that my code could parametrized and not fixed to just numbers 1 and 2, or just + and -, etc)
What intuitions, insights and lessons can I get from this exercise?


I know that this is a massively contrived situation, but they serve me as an exercise to understand the language better.
Using pattern matching you can write:

invert 1 = 2
invert 2 = 1
invert n = 0 -- Error would be better in my opinion




You can inline 6 and 2 instead of taking them from a single item list.