haskell,如何处理不匹配的类型 - monads

时间:2016-04-20 11:10:59

标签: haskell monads

我无法处理以下问题:
我有一些功能,返回foo :: a -> b -> ErrorT String IO Int
我知道它会返回IO (Either String Int) 尽管如此,我还有更复杂的功能,它返回:

bar :: a -> b -> StateT Char (ReaderT Char (ErrorT String IO)) Int

此函数bar调用函数foo。我要跟随的事情:
如果foo调用throwError bar也会引发相同的错误。如果foo返回Int bar,则返回Int

但是,由于不匹配类型,这是可能的。

我不知道如何以优雅的方式做到这一点。 我认为我的transformator monad订单不行。

2 个答案:

答案 0 :(得分:2)

这样的事情应该有效:

bar x y = do
  -- some code
  z <- lift $ lift $ foo x y
  -- some more code
  return z

答案 1 :(得分:2)

我通常使用的是我所称的 Transformer Monad Classes

{-# LANGUAGE FlexibleContexts #-}

import Control.Monad.Reader
import Control.Monad.State

inner :: ReaderT Char IO Int
inner = do
    a <- ask
    lift $ print a
    return 5

inner' :: (MonadReader Char m, MonadIO m) => m Int
inner' = do
    a <- ask
    liftIO $ print a
    return 5

outer :: StateT Char (ReaderT Char IO) Int
outer = do
    a <- get

    b <- lift $ inner    -- need to lift
    c <- inner'          -- no need to lift

    lift . lift $ print "need to lift twice to get to IO"            

    return 5

main = runReaderT (runStateT outer 'b') 'a'

让我们把它分解;第一个inner函数具有您需要直接抬起以便使用它的具体类型。但是,如果您参数化它只在签名中留下 capabilities ,则可以跳过提升,只要它在堆栈中明确地获取此功能。在这种情况下,它很明确,因为StateT Char (ReaderT Char IO) Int只有MonadIO的一个实例(来自IO)和MonadReader Char的一个实例(来自ReaderT ... Char })。

现在,只要实例清晰,您需要制作多少部电梯并不重要!考虑:

outer' :: ErrorT String (StateT Char (ReaderT Char IO)) Int
outer' = do
    a <- inner'               -- still no need to lift!
    b <- lift . lift $ inner  -- need to double lift in this case
    return 5

最后可能不明显的是outer的签名也可以用通用的方式表达:

outer :: (MonadState Char m, MonadReader Char m, MonadIO m) => m Int

它仍然可以在没有任何提升的情况下工作(对于IO操作没有liftIO,因为像print这样的函数是根据IO定义的,而不是MonadIO m。比较,像askget这样的函数是根据相应的MonadX类来定义的,这样我们就可以跳过提升了。