as Thomas pointed out off-list, the transformation sequence as given is not type-preserving. i even documented that problem in my email, because i thought the type was dodgy, but forgot to track it down before posting. so here are the changes. a good demonstration that
"does it still compile?" is not a sufficient test for refactoring!-)

claus

 to prepare for our next step, we apply lift to all barebones STM
 operations, readTVar, write, empty, nullT. at this stage, our types
 (asking ghci, with :t dmin') are slightly redundant:

dmin' :: (MonadTrans t1, Monad (t1 STM)) => TVar (Trie t) -> t1 STM (Maybe (t, Bool))

 since our particular MonadTrans, MaybeT, already wraps results in
 Maybe, this is one level of Maybe too much. so, when we remove our
 local definitions of mplus and >>> (replacing >>> with >>=), we remove
 that extra layer of Maybe, by removing the redundant (Just _) in
returns, and by replacing 'return Nothing' with 'mzero'.

we also need to take into account that the second readTVar already returns a Maybe, so we only need to wrap it in MaybeT, without applying the full lift.

we could now declare the type as

   dmin' :: TVar (Trie t) -> MaybeT STM (Maybe t, Bool)

there's that dodgy type. it should just be:

   dmin' :: TVar (Trie t) -> MaybeT STM (t, Bool)
 after all that refactoring, the code should look something like this:

dmin p = maybe (error "dmin: no values") (return . fst) =<< runMaybeT (dmin' p)

   dmin' p = do
       t <- lift $ readTVar p
       case t of
           Empty -> mzero
Trie l m r -> (dmin' l >>=
                       (\ (v,e) -> do
                         case e of
                             True -> do
                                 me <- lift $ empty m
                                 re <- lift $ nullT r
                                 lift $ write m p (v,me && re)
                             False -> return (v,e)))
               `mplus` (((lift $ readTVar m) >>=

it was the return-wrapping of lift that introduced the extra Maybe here. this TVar already holds Maybes, so this should just be:

               `mplus` (((MaybeT $ readTVar m) >>=

 next, we can make use of the fact that pattern match failure in
 do-notation invokes fail in the monad, by defining 'fail msg = mzero'
 in our wrapped monad, and by pattern matching directly on the result
 of the first readTVar' (we only need the Trie-case, the other case
 will fail to match, leading to mzero, which is what we wanted anyway).

we can also use this feature to replace the "half-lifted" second
readTVar with a fully lifted readTVar' followed by a pattern match
on 'Just v'.

   --------------------------------------------- final version
dmin p = maybe (error "dmin: no values") (return . fst) =<< runMaybeT (dmin' p)

   dmin' p = do
       Trie l m r <- readTVar' p
       (do (v,e) <- dmin' l
           (do guard e
               me <- empty m
               re <- nullT r
               write m p (v,me && re))
            `mplus` return ((v,e)))
        `mplus` (do v <- readTVar' m

by employing pattern-match failure handling, this can become:

       `mplus` (do Just v <- readTVar' m

                    re <- nullT r
                    write m p (v,re))
        `mplus` (do (v,e) <- dmin' r
                    when e $ writeTVar' p Empty
                    return ((v,e)))
        `mplus` error "emit nasal daemons"
       where
       readTVar'  var     = lift $ readTVar var
       writeTVar' var val = lift $ writeTVar var val

       write m p (v,False) = lift $ writeTVar m Nothing >> return ((v,False))
       write m p (v,True ) = lift $ writeTVar p Empty   >> return ((v,True))

       nullT :: Monad m => TriePtr t -> m Bool
       nullT t = undefined

       empty m = lift $ liftM isNothing $ readTVar m

   data MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

   instance Monad m => Monad (MaybeT m) where
     return  = MaybeT . return . Just
     a >>= b = MaybeT $ runMaybeT a >>= maybe (return Nothing) (runMaybeT . b)
     fail msg= mzero

   instance Monad m => MonadPlus (MaybeT m) where
     mzero       = MaybeT $ return Nothing
     a `mplus` b = MaybeT $ runMaybeT a >>= maybe (runMaybeT b) (return . Just)

   instance MonadTrans MaybeT where
     lift m = MaybeT $ m >>= return . Just

   --------------------------------------------- final version

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to