I'm trying to implement insertion sort in Haskell on Data.Array.ST
, with a monadic comparison function. i.e., sortByM :: (Monad m, Ix i, Enum i, Eq i) => (e -> e -> Ordering) -> STArray s i e -> m (ST s ())
. Here's my code, for context:
import Data.Ord
import Data.Foldable
import Data.Array.ST
import Control.Monad.ST
sortByM :: (Monad m, Ix i, Enum i, Eq i)
=> (e -> e -> m Ordering)
-> STArray s i e -> m (ST s ())
sortByM cmp xs = sequence_ <$> traverse (bubbleBack xs) [start..end]
where
(start, end) = runST $ getBounds xs
bubbleBack :: (Monad m, Ix i, Enum i, Eq i)
=> STArray s i e -> i -> m (ST s ())
bubbleBack ary = loopM $ fmap (mapLeft runST) . bubbleBackOnce ary
bubbleBackOnce :: (Monad m, Ix i, Enum i, Eq i)
=> STArray s i e
-> i -> m (Either (ST s i) (ST s ()))
bubbleBackOnce ary bubble =
if bubble == start
then return $ Right $ return ()
else do
let elem = runST $ readArray ary bubble
let prev = runST $ readArray ary (pred bubble)
ordering <- cmp elem prev
return $ if ordering == LT
then Left $ do
writeArray ary bubble prev
writeArray ary (pred bubble) elem
return $ pred bubble
else Right $ return ()
-- ...
mapLeft :: (a -> r) -> Either a b -> Either r b
mapLeft f (Left x) = Left (f x)
mapLeft _ (Right x) = Right x
The problem I'm running into, as I understand it, is that loopM
's first parameter is expected to have type ST s i -> m (Either i (ST s ()))
, but because of runST
's method of hiding ST
's internal state, fmap (mapLeft runST) . bubbleBackOnce ary
has type (forall s'. ST s' i) -> Either i (ST s i)
. This is my first time using ST, so I'm not sure how to address this. Additionally, this is for a pet project, so I'm trying to avoid relying on existing libraries that would simplify things, e.g. STMonadTrans.