除了死代码检测

时间:2017-07-15 09:15:50

标签: haskell monad-transformers

ExceptT Monad变换器堆栈中,throwE之后的绑定表达式将无法执行。

  1. 这是真的吗?
  2. 如果是这样,我如何配置编译器或我的工具来检测死代码,例如这个例子?
  3. {-# LANGUAGE
        GeneralizedNewtypeDeriving
    #-}
    
    import Control.Monad.Except (MonadError (..), MonadIO, liftIO)
    import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE)
    
    newtype MyTrans e a = MyTrans { unMyTrans :: ExceptT e IO a  } 
      deriving (Functor, Applicative, Monad, MonadIO, MonadError e)
    
    throwMT :: e -> MyTrans e a
    throwMT = MyTrans . throwE
    
    runMyTrans :: MyTrans e a -> IO (Either e a)
    runMyTrans = runExceptT . unMyTrans
    
    comp = do
      liftIO $ putStrLn "Starting Computation"
      throwMT "Some Error"
      -- dead code from this line
      liftIO $ putStrLn "Ending Computation"
      return ()
    
    
    main = print =<< runMyTrans comp
    

2 个答案:

答案 0 :(得分:3)

  1. 这是真的。
  2. 我知道目前Haskell没有可用的死代码消除工具。

答案 1 :(得分:3)

让我们忽略MyTrans新型噪音,然后看看

throwE x >>= f :: ExceptT e IO a

深入研究Control.Monad.Trans.Except,我们看到了

newtype ExceptT e m a =
  ExceptT {runExceptT :: m (Either e a)}

throwE :: Monad m => e -> ExceptT e m a
throwE e = ExceptT (pure (Left e))

instance Monad m => Monad (ExceptT e m) where
  m >>= f = ExceptT $ do
    res <- runExceptT m
    case res of
      Left e -> pure (Left e)
      Right r -> runExceptT (f r)

内联这些(GHC喜欢做的事情),我们看到throwE e >>= f

ExceptT $ do
  res <- pure (Left e)
  case res of
    Left e -> pure (Left e)
    Right r -> runExceptT (f r)

考虑到这一点对于一个任意的monad,GHC不会再进一步​​,因为它不知道m是一个守法的monad并且不会寻找& #34;自由定理&#34;这可能有所帮助。

但是,这不是一个任意的monad!这是IO

newtype IO a =
  IO (State# RealWorld
            -> (# State# RealWorld, a #))
unIO :: IO a -> State# RealWorld
            -> (# State# RealWorld, a #)
unIO (IO f) = f

instance Monad IO where
  return a = IO $ \s -> (# s, a #)
  IO m >>= f = IO $ \s ->
    case m s of
      (# s', a #) -> unIO (f a) s'

左身份法可以被证明&#34;通过优化获得IO

return a >>= g
= inlining
IO $ \s ->
  case (\t -> (# t, a #)) s of
    (# s', r #) -> unIO (g r) s'
= beta reduction
IO $ \s ->
  case (# s, a #) of
    (# s', r #) -> unIO (g r) s'
= case of known constructor
IO $ \s -> unIO (g a) s
= (assuming g has arity at least two, which it will here)
g a

应用此信息,throwE e >>= f变为

ExceptT $
  (\res -> case res of
    Left e -> pure (Left e)
    Right r -> runExceptT (f r)) (Left e)

Beta减少并应用已知构造函数的情况

ExceptT $ pure (Left e)

完全消除了死密码。