OK check this out.
Select fragments copied from the hashed package and pragmas of the voodoo language
{-
OK, so now we can take a list of something Hashable and turn it into Int . I have provided instances of Char and Integral a ; larger and better instances are in hashed packaging, which also allows salt and more.
That’s all we can do with a number generator.
genFromHashable = mkStdGen . hash
So now the fun part. Let a function be written that takes a random number generator, a comparator function, and a list. Then we sort the list by contacting the generator to select the reference element, and the comparator to split the list.
qSortByGen _ _ [] = [] qSortByGen gf xs = qSortByGen g'' fl ++ mid ++ qSortByGen g''' fr where (l, mid, r) = partition (`f` pivot) xs pivot = xs !! (pivotLoc `mod` length xs) (pivotLoc, g') = next g (g'', g''') = split g' partition f = foldl' step ([],[],[]) where step (l,mid,r) x = case fx of LT -> (x:l,mid,r) EQ -> (l,x:mid,r) GT -> (l,mid,x:r)
Library Functions : next captures Int from the generator and creates a new generator. split deploys the generator to two different generators.
My functions : partition uses f :: a -> Ordering to split the list into three lists. If you know the folds, this should be perfectly clear. (Note that it does not preserve the initial order of elements in the sublists; it changes them. Using foldr can fix this if this is a problem). qSortByGen works the same way as I said: consult the generator for the fulcrum, split the list, expand the generator for use in two recursive calls, recursively sort the left and right sides and combine it all together.
Convenient features are easy to compose here.
qSortBy f xs = qSortByGen (genFromHashable xs) f xs qSort = qSortBy compare
Pay attention to the final signature of the function.
ghci> :t qSort qSort :: (Ord a, Hashable a) => [a] -> [a]
The type inside the list must implement both Hashable and Ord . There is a "pure" function that you requested, with one logical additional requirement. More general functions are less stringent in their requirements.
ghci> :t qSortBy qSortBy :: (Hashable a) => (a -> a -> Ordering) -> [a] -> [a] ghci> :t qSortByGen qSortByGen :: (System.Random.RandomGen t) => t -> (a -> a -> Ordering) -> [a] -> [a]
Final notes
qSort will behave exactly the same for all inputs. "Random" rotation selection. actually deterministic. But it is obscured by hashing the list, and then by sowing the random number generator, which makes it “random” enough for me.;)
qSort also works only for lists shorter than maxBound :: Int , which, according to ghci, are 9,223,372,036,854,775,807. I thought there would be a problem with negative indices, but in my ad-hoc testing I have not come across this yet.
Or you can just live with the IO monad for a "true" coincidence.
qSortIO xs = do g <- getStdGen -- add getStdGen to your imports return $ qSortByGen g compare xs ghci> :t qSortIO qSortIO :: (Ord a) => [a] -> IO [a] ghci> qSortIO "Hello world" " Hdellloorw" ghci> qSort "Hello world" " Hdellloorw"