The quicksort algorithm in Haskell I am learning Haskell programming language mainly from this source. And there I have encouraged with &quot;an elegant&quot; realization of the quicksort sorting algorithm (the Quick, sort! section). Here it is:
Book's implementation
quicksort :: ( Ord a ) = &gt; [ a ] -&gt; [ a ]
quicksort [] = []
quicksort ( x : xs ) =
    let smallerSorted = quicksort [ a | a &lt;- xs , a &lt;= x ]
        biggerSorted = quicksort [ a | a &lt;- xs , a &gt; x ]
    in smallerSorted ++ [ x ] ++ biggerSorted

The problem with this implementation (as I think) is doubled number of comparisons inside the let binding every time. Isn't the fact that those of a's that already in the smallerSorted list cannot be in the biggerSorted list, so we don't need to compare x with them anymore.
In order to &quot;improve&quot; the above approach I have written my own implementation, where I use auxiliary local function split that splits a list into two parts: less than (or equal to) x and greater than x.
My implementation
quick_sort :: (Ord a) =&gt; [a] -&gt; [a]
quick_sort []       = [] --edge condition 
quick_sort (x : xs) =    --general condition
    let (lt, gt) = split x xs
    in  (quick_sort lt) ++ [x] ++ (quick_sort gt) 
    where
        --split function is used to split a list
        --into two sublists: one for elements
        --less or equal (lt) than some value - x
        --and one for those that greater (gt) than x
        --NOTE: split function is also recursive
        split x [] = ([], []) --edge condition
        split x (h : hs)      --general condition
            | h &lt;= x    =
                let (lt, gt) = split x hs
                in ( (h : lt), gt )
            | otherwise =
                let (lt, gt) = split x hs
                in ( lt, (h : gt) )

P.S.
For a while I am not able to compare two approaches, but on the worst cases (when list to sort is already sorted) it seems that my implementation is a bit slower.
First of all, great that you used type annotations, as they're sometimes missing. However, there are some remarks:


"edge" and "general"` condition are usually called "base case" and "recursive case"
"base case" and "recursive case" aren't commented in Haskell code, as they are everywhere
split is generic enough to be defined at top-level
split has a little bit of duplication that we can remove with a single where clause
functions are usually named in camelCase instead of snake_case, so we'd call it quickSort.


If we apply that we end up with

quickSort :: (Ord a) =&gt; [a] -&gt; [a]
quickSort []       = []
quickSort (x : xs) =
    let (lt, gt) = split x xs
    in  (quickSort lt) ++ [x] ++ (quickSort gt) 

split :: (Ord a) =&gt; a -&gt; [a] -&gt; ([a], [a])
split x [] = ([], [])
split x (h : hs)
    | h &lt;= x    = ( h : lt,     gt)
    | otherwise = (     lt, h : gt)
    where
        (lt, gt) = split x hs


That being said, split seems so helpful that there should be some function already in the standard library. And it turns out there is, namely partition :: (a -&gt; Bool) -&gt; [a] -&gt; ([a],[a]) from Data.List:

split :: (Ord a) =&gt; a -&gt; [a] -&gt; ([a], [a])
split x = partition (&lt;= x)


That's a lot cleaner than our own implementation. So how do we find partition if we don't know about it yet? We use Hoogle. However, a query for Ord a =&gt; a -&gt; [a] -&gt; ([a],[a]) does not yield partition. It's sometimes a good idea to generalize the query a little bit, for example to [a] -&gt; ([a],[a]), as Hoogle also scans parts of the type signature. That way, we find partition.

If we're going for brevity, we can then inline split to gain

-- | Sorts the given list in an ascending order.
quickSort :: (Ord a) =&gt; [a] -&gt; [a]
quickSort []       = []
quickSort (x : xs) =
    let (lt, gt) = partition (&lt;= x) xs
    in  (quickSort lt) ++ [x] ++ (quickSort gt)


but that's up to personal preference, as the compiler will inline split anyway.