N Queens with Haskell
TL;DR:


Don't repeat yourself. If you notice that code is very similar, ask whether you can use that similarity.
Express functions with other functions.
Avoid length in recursive calls, as it will add another factor n to your time complexity


Overall, still well done.

Naming and (time) complexity

Let's start by looking at the types. Well, you have type signatures at every top-level binding, which is a great start. I think that the name Solution is slightly misleading, since you call the variable b for Board. But naming is hard, so let's focus on other parts.

extend :: Solution -&gt; Int -&gt; Maybe Solution
extend b new = go 0 b
  where
    n = length b                                       -- 1
    go _ []  =  Just (b ++ [new])                      -- 2
    go i (x:xs)
      | x == new                      = Nothing        
      | abs (i - n) == abs (x - new)  = Nothing        
      | otherwise                     = go (i+1) xs


extend has two small problems. First, xs ++ [e] (see 2 above) is linear (\$\mathcal O(n)\$), whereas e : xs is constant (\$\mathcal O(1)\$). If possible, we  should prefer the second variant. And length b is also linear (see 1 above). But if we fix 2 we don't need n anymore:

extend :: Solution -&gt; Int -&gt; Maybe Solution
extend bs new = go 1 bs
  where
    go _ [] = Just (new : bs)
    go i (x:xs)
      | x == new           = Nothing
      | i == abs (x - new) = Nothing
      | otherwise          = go (i + 1)


Note that I've renamed b to bs, since the suffix s indicates multiple elements.

Don't repeat yourself

Although yout extend and extend' differ, the logic in go is almost the same. That's a sign that you can re-use the code, so let's try that:

placeable :: Int -&gt; Solution -&gt; Bool
placeable n xs = null [ x | (i,x) &lt;- zip [1..] xs, x == n || abs (x - n) == i]
-- same as
placeable' :: Int -&gt; Solution -&gt; Bool
placeable' n xs = go 1 xs
  where
    go _ []     = True
    go i (x:xs) = x /= n &amp;&amp; abs (x - n) == i &amp;&amp; go (i + 1) xs


Now we can simplify extend and extend':

extend :: Solution -&gt; Int -&gt; Maybe Solution
extend bs new = if placeable new bs 
                  then Just (new : bs) 
                  else Nothing

extend' :: Solution -&gt; Int -&gt; [Solution]
extend' bs new = if placeable new bs 
                   then [new : bs]
                   else []


Feel free to use guard syntax instead.

Reuse functions

If you have a function listToMaybe :: [a] -&gt; Maybe a, you don't need to write extend:

extend :: Solution-&gt; Int -&gt; Maybe Solution
extend bs new = listToMaybe (extend' bs new)


We can do the same for nQueens and get rid of extend completely:

nQueens :: Int -&gt; Maybe Solution
nQueens n = listToMaybe (nQueens' n)


But you need to be lazy enough for that.

Use knowledge you already have

In fullExtend, you'll check whether you already reached the correct length:

fullExtend :: Int -&gt; Solution -&gt; Maybe Solution
fullExtend n partialSolution
  | length partialSolution == n     = Just partialSolution             -- here
  ...


However, that's again \$\mathcal O(n)\$. But we know the length of our current solution; almost. It's the number of recursive calls:

  ...
    propose x Nothing   = extend partialSolution x &gt;&gt;= fullExtend n  -- one element more


Also n does not change. This calls for a go-like function:

fullExtend :: Int -&gt; Solution -&gt; Maybe Solution
fullExtend n = go n
  where
    go 0 partialSolution = Just partialSolution
    go k partialSolution = foldr propose Nothing [0..(n-1)]
      where
        propose _ (Just xs) = Just xs
        propose x Nothing   = extend partialSolution x &gt;&gt;= go (k - 1)


We now count down from n to 0 in go. Whenever we reached 0, we have all n elements. This removes the need for length. At this point fullExtend is almost the same as nQueens, so we can get rid of it:

nQueens :: Int -&gt; Maybe Solution
nQueens n = go n []
  where
    go 0 partialSolution = Just partialSolution
    go k partialSolution = foldr propose Nothing [0..(n-1)]
      where
        propose _ (Just xs) = Just xs
        propose x Nothing   = extend partialSolution x &gt;&gt;= go (k - 1)


We can use the same approach for fullExtend'. First, get rid of length:

fullExtend' :: Int -&gt; Solution -&gt; [Solution]
fullExtend' n partialSolution = go n partialSolution
  where
    go 0 xs = [xs]
    go k xs = [0..(n-1)] &gt;&gt;= (\x -&gt; extend' partialSolution x &gt;&gt;= go (k - 1))


Then rewrite nQueens':

nQueens' :: Int -&gt; [Solution]
nQueens' n = go n []
  where
    go 0 xs = [xs]
    go k xs = [0..(n-1)] &gt;&gt;= (\x -&gt; extend' xs x &gt;&gt;= go (k - 1))


By the way, this looks a little bit nicer with do notation:

nQueens' :: Int -&gt; [Solution]
nQueens' n = go n []
  where
    go 0 bs = [bs]
    go k bs = do 
              x  &lt;- [0..(n-1)]
              xs &lt;- extend' bs x
              go (k - 1) xs


But that's up to you to decide.

Your propose question


  Can the propose function be expressed in a more elegant way?


You can use Maybe's Alternative instance:

propose x solution = solution &lt;|&gt; (extend partialSolution x &gt;&gt;= go (k  -1))


However, that's not really necessary if you rewrite nQueens

All at once

If we also inline extend, we end up with the following code for the list variant:

import Data.Maybe (listToMaybe)

type Board = [Int]

nQueens :: Int -&gt; Maybe Board
nQueens = listToMaybe . nQueens'

nQueens' :: Int -&gt; [Board]
nQueens' n = go n []
  where
    go 0 bs = [bs]
    go k bs = concatMap (go (k - 1)) [ x : bs | x &lt;- [1..n], placeable x bs]

    placeable n xs = null [ i | (i,x) &lt;- zip [1..] xs, n == x || abs (n - x) == i]


We could rewrite go above also as

    go k bs = do
              x &lt;- [1..n]
              guard (placeable x bs)
              go (k - 1) (x : bs)


or

    go k bs = [1..n] &gt;&gt;= (\x -&gt; guard (placeable x xs) &gt;&gt; go (k - 1) (x : bs)


but that's only personal preference, they all work the same.