Using Exceptions in Transformer Monad

时间:2018-03-25 21:07:17

标签: haskell

I have a question regarding use of Exceptions with a transformer stack. I am a trying to develop some networking software, specifically implement the GTP control protocol on S5 interface. I am finding it difficult to get Exceptions work the transformer stack.

import Control.Monad (unless)
import Control.Exception
....
import Control.Monad.Trans.State.Strict
import Control.Monad.Trans.Except
...
data GtpcModSt = GtpcModSt { sock             :: Socket
                       , rcvdBytes        :: BS.ByteString
                       , s5cTeidKey     :: Word32
                       ---- ....
                       } --deriving (Show)

type EvalGtpC a = (StateT GtpcModSt (ExceptT GtpcExceptions IO )) a
                  -- deriving (Functor, Applicative, Monad) 



gtpcProcess = loop
       where loop = do
             rcvAndProcessGtpc   `catch` (\e -> do
                                      print "Exception handler"
                                      print (e :: SomeException))
            loop



 rcvAndProcessGtpc ::  EvalGtpC ()
 rcvAndProcessGtpc = do
                    sock <- gets sock
                    (msg, addr) <- liftIO $ recvFrom sock 1000
                    modify (\x -> x {rcvdBytes = msg, sndrAddr = addr})
                    processMsg
processMsg :: EvalGtpC ()
 processMsg = do
            --  validateSrc
           -----
          --....
            msg <- gets gtpMsg
            processGtpc $ msgType msg

 -- createSessionRequest
  processGtpc :: Word8 -> EvalGtpC ()
  processGtpc 32 = do
         myState@GtpcModSt {..} <- get
         .....

         sessParams <- return $ foldl ieInfo (SessionParams { imsi = Nothing
                              , mei = Nothing
                              , msisdn = Nothing
                              , senderFteidKey = Nothing
                              , senderIpV4Addr = Nothing
                              , senderIpV6Addr = Nothing
                              , pgwFteidKey      = Nothing
                              , pgwIpV4Addr   = Nothing
                              , pgwIpV6Addr   = Nothing
                              , apn           = Nothing
                              , paaPdnType    = Nothing
                              , pco           = Nothing
                              , bearerContext = []
                              , unDecodedIe   = []
                              , unSupportedIe = []
                              }) $ msgIeList gtpMsg


        ueApn <-return $ fromMaybe (throwE BadIe) (apn sessParams)
        apnCfg <- return $ fromMaybe (throw BadIe) $ Map.lookup ueApn apnProfile
        thisSndrFteidKey <-return $  fromMaybe (throw BadIe) (senderFteidKey sessParams)   

I think that I should use throwE/catchE from Control.Monad.Trans.Except. However, throwE does not even compile when used with my transformer monad, as shown here:

apnCfg <- return $ fromMaybe (throw UnknownApn) $ Map.lookup ueApn apnProfile

Using throw from Control.Exception gets past the compilation stage but I am not sure it will work. Should I not be using Exception in a transformer monad that has IO as its base?

1 个答案:

答案 0 :(得分:1)

  

我认为我应该使用throwE中的catchE / Control.Monad.Trans.Except。但是,throwE在与变换器monad一起使用时甚至无法编译,如下所示:

apnCfg <- return $ fromMaybe (throw UnknownApn) $ Map.lookup ueApn apnProfile
     

使用throw中的Control.Exception已经过了编译阶段,但我不确定它是否可行。

这可以通过遵循类型来解决。在你的do-block中,我们有:

-- I won't use the synonym here, for the sake of explicitness:
return :: a -> StateT GtpcModSt (ExceptT GtpcExceptions IO) a

throwE的类型是:

throwE :: Monad m => e -> ExceptT e m a 

就是这样,你想要的是:

apnCfg <- maybe (lift $ throwE UnknownApn) return $ Map.lookup ueApn apnProfile

首先,如果您没有投掷,则只需要returnmaybefromMaybe表达更方便)。其次,throwE生成ExceptT计算,您需要lift到外层StateT层。您可以直接使用 mtl 而不是转换器来隐式lift。为此,请从...中更改导入

import Control.Monad.Trans.State.Strict
import Control.Monad.Trans.Except

......来:

import Control.Monad.State.Strict
import Control.Monad.Except

然后您可以简单地编写(使用MonadError中的throwError方法):

apnCfg <- maybe (throwError UnknownApn) return $ Map.lookup ueApn apnProfile