2

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.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Evelyz
  • 71
  • 1
  • 4
  • 2
    `Monad m` isn't enough. Consider if `m` were a backtracking monad like `[]` which would render `ST` unsafe due to its "teleportation" abilities. I suspect you need [`Primitive`](http://hackage.haskell.org/package/primitive-0.7.0.0/docs/Control-Monad-Primitive.html). – luqui Oct 28 '19 at 07:39
  • That use of `runST` also looks really fishy. It shouldn't be compatible with that type signature. The scoping on the `s` type variable is wrong. – Carl Oct 29 '19 at 02:27

0 Answers0