Determine children within edge and return a list of tuple pairs
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.