Determine children within edge and return a list of tuple pairs I've been studying hard and asking a lot of questions - I finally came a cross an exercise in LYAH that looked like it was easy enough but a perfect candidate for practicing.

The below program has a bunch of helper funcs in there, but the one of interest is neighbors/3.  It takes a Pair type and Edge number, and a BiMorph type. It then determines all of the children within that edge and returns it as a list of tuple pairs.

I would like a more experienced Haskeller to come in and tear it apart or to give me constructive criticism.

import Control.Monad
import Control.Applicative

data Pair a    = Pair a a deriving (Show, Eq)
data BiMorph a = BiMorph Func Func

type Neighbors = [(Int, Int)]
type Matrix    = [[Int]]
type Func      = (Int -&gt; Int -&gt; Int)

instance Functor Pair where
   fmap f (Pair a b) = Pair (f a) (f b)

instance Applicative Pair where
   pure a = Pair a a
   (Pair fa fb) &lt;*&gt; (Pair a b) = Pair (fa a) (fb b)

matrix :: Int -&gt; Int -&gt; Matrix
matrix xl yl = replicate yl $ [1..xl]

cartesianPairs :: Func -&gt; Pair Int -&gt; [Pair Int]
cartesianPairs f p =
    [(f &lt;$&gt; (Pair x y) &lt;*&gt; p) | x &lt;- [1,0], y &lt;- [0,1], (x,y) /= (0,0)]

cartesianDoubles :: Func -&gt; Func -&gt; Pair Int -&gt; [Pair Int]
cartesianDoubles f g (Pair x y) = [Pair (f x 1) (g y 1), Pair (g x 1) (f y 1)]

dropP :: Pair Int -&gt; (Int, Int)
dropP (Pair x y) = (abs x, abs y)

liftP :: (Int, Int) -&gt; Pair Int
liftP (x, y) = Pair x y

neighbors :: Pair Int -&gt; Int -&gt; BiMorph Int -&gt; Neighbors
neighbors p e (BiMorph f g) = 
    map dropP $ concat [(cartesianPairs f p), (cartesianPairs g p), (cartesianDoubles f g p)]
        &gt;&gt;= (\(Pair x' y') -&gt; guard (x' &lt;= e &amp;&amp; y' &lt;= e)
            &gt;&gt; return (Pair x' y'))
Run hlint tool on your source to help you with getting rid of redundant brackets, $ and other minor suggestions.

In neighbors your using of guard in list monad can be changed to filter function:

neighbors :: Pair Int -&gt; Int -&gt; BiMorph Int -&gt; Neighbors
neighbors p e (BiMorph f g) = map dropP $ filter ff $ concat [cartesianPairs f p, cartesianPairs g p, cartesianDoubles f g p] where
    ff (Pair x' y') = x' &lt;= e &amp;&amp; y' &lt;= e


You can also hardcode the list into cartesianPairs as it's short:

cartesianPairs :: Func -&gt; Pair Int -&gt; [Pair Int]
cartesianPairs f p =
    [f &lt;$&gt; Pair x y &lt;*&gt; p | (x, y) &lt;- [(1,0), (0,1), (1,1)]]


You can even use pairs directly:

cartesianPairs :: Func -&gt; Pair Int -&gt; [Pair Int]
cartesianPairs f p =
    [f &lt;$&gt; pair &lt;*&gt; p | pair &lt;- [Pair 1 0, Pair 0 1, Pair 1 1]] 


I also have an impression that cartesianPairs can be further simplified but I couldn't find anything that has clear meaning. Here are two attempts:

cartesianPairs f p =
     (&lt;*&gt; p) &lt;$&gt; (fmap . fmap) f [Pair 1 0, Pair 0 1, Pair 1 1]

cartesianPairs f p =
     (&lt;*&gt; p) . fmap f &lt;$&gt; [Pair 1 0, Pair 0 1, Pair 1 1]


cartesianDoubles can be shrinked the same way if you want:

cartesianDoubles f g (Pair x y) = [Pair (ff x) (gg y), Pair (gg x) (ff y)] where
    ff x = f x 1
    gg x = g x 1

cartesianDoubles f g p = [Pair ff gg &lt;*&gt; p, Pair gg ff &lt;*&gt; p] where
    ff x = f x 1
    gg x = g x 1

cartesianDoubles f g p = (&lt;*&gt; p) &lt;$&gt; [Pair ff gg, Pair gg ff] where
    ff x = f x 1
    gg x = g x 1


Yet another improvement. You can notice that there are versions of cartesianDoubles and cartesianPairs sharing the same (&lt;*&gt; p) &lt;$&gt; part:

cartesianPairs f p =
    (&lt;*&gt; p) &lt;$&gt; (fmap . fmap) f [Pair 1 0, Pair 0 1, Pair 1 1]

cartesianDoubles f g p = (&lt;*&gt; p) &lt;$&gt; [Pair ff gg, Pair gg ff] where
    ff x = f x 1
    gg x = g x 1


Then notice that &lt;$&gt; in (&lt;*&gt; p) &lt;$&gt; is for Functor [] instance, so it's just plain map, and map has a property of commuting withconcat:map f . concat == concat . map f`.

So you can move the common fragment out:

cartesianPairs f p =
    (fmap . fmap) f [Pair 1 0, Pair 0 1, Pair 1 1]

cartesianDoubles f g p = [Pair ff gg, Pair gg ff] where
    ff x = f x 1
    gg x = g x 1

neighbors :: Pair Int -&gt; Int -&gt; BiMorph Int -&gt; Neighbors
neighbors p e (BiMorph f g) = map dropP $ filter ff $ map (&lt;*&gt; p) $ concat [cartesianPairs f p, cartesianPairs g p, cartesianDoubles f g p] where
    ff (Pair x' y') = x' &lt;= e &amp;&amp; y' &lt;= e


Now p argument is not used:

cartesianPairs f =
    (fmap . fmap) f [Pair 1 0, Pair 0 1, Pair 1 1]

cartesianDoubles f g = [Pair ff gg, Pair gg ff] where
    ff x = f x 1
    gg x = g x 1

neighbors :: Pair Int -&gt; Int -&gt; BiMorph Int -&gt; Neighbors
neighbors p e (BiMorph f g) = map dropP $ filter ff $ map (&lt;*&gt; p) $ concat [cartesianPairs f, cartesianPairs g, cartesianDoubles f g] where
    ff (Pair x' y') = x' &lt;= e &amp;&amp; y' &lt;= e


If you want, you can convert neighbors to use list comprehensions:

neighbors :: Pair Int -&gt; Int -&gt; BiMorph Int -&gt; Neighbors
neighbors p e (BiMorph f g) = 
    [ dropP xx 
    | x &lt;- concat [cartesianPairs f, cartesianPairs g, cartesianDoubles f g]
    , let xx @ (Pair x' y') = x &lt;*&gt; p
    , x' &lt;= e &amp;&amp; y' &lt;= e ] 


Or even

neighbors :: Pair Int -&gt; Int -&gt; BiMorph Int -&gt; Neighbors
neighbors p e (BiMorph f g) = 
    [ dropP xx 
    | xx @ (Pair x' y') &lt;- (&lt;*&gt; p) &lt;$&gt; 
        concat [cartesianPairs f, cartesianPairs g, cartesianDoubles f g] 
    , x' &lt;= e &amp;&amp; y' &lt;= e ] 


But I find it less clear.