无法将类型a与a1匹配,但类型看起来相同

时间:2018-12-31 19:35:14

标签: haskell

我正在学习Haskell,编译器给了我一个我不太明白的错误。它说期望的和实际的类型不匹配,但是对我来说它们看起来是相同的。谁能帮助我了解此错误试图说明什么?

/app/app/Main.hs:75:11: error:
    • Couldn't match type ‘a’ with ‘a1’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          app :: forall a.
                 (ReaderT (W.Options -> String -> IO (Response BL.ByteString)) IO a
                  -> IO a)
                 -> IO Middleware
        at /app/app/Main.hs:68:1-99
      ‘a1’ is a rigid type variable bound by
        a type expected by the context:
          forall a1.
          ReaderT (W.Options -> String -> IO (Response BL.ByteString)) IO a1
          -> IO a1
        at /app/app/Main.hs:75:3-12
      Expected type: ReaderT
                       (W.Options -> String -> IO (Response BL.ByteString)) IO a1
                     -> IO a1
        Actual type: ReaderT
                       (W.Options -> String -> IO (Response BL.ByteString)) IO a
                     -> IO a
    • In the first argument of ‘spockT’, namely ‘(r)’
      In the expression: spockT (r)
      In a stmt of a 'do' block: spockT (r) $ routes apiKey template
    • Relevant bindings include
        r :: ReaderT
               (W.Options -> String -> IO (Response BL.ByteString)) IO a
             -> IO a
          (bound at /app/app/Main.hs:69:5)
        app :: (ReaderT
                  (W.Options -> String -> IO (Response BL.ByteString)) IO a
                -> IO a)
               -> IO Middleware
          (bound at /app/app/Main.hs:69:1)
   |
75 |   spockT (r) $ routes apiKey template
   |           ^

让我更加困惑的是,我所做的只是将函数从直接调用变为参数。

下面失败的代码(简化),但是如果我内联runner,即将其作为app的参数删除,则一切正常。

main :: IO ()
main = do
  runSpock 8080 $ app runner

runner :: ReaderT (W.Options -> String -> IO (Response BL.ByteString)) m a -> m a
runner r = runReaderT r W.getWith

app :: (ReaderT (W.Options -> String -> IO (Response BL.ByteString)) IO a -> IO a) -> IO Middleware
app runner = do
  spockT (runner) $ routes

routes :: MonadIO m => SpockCtxT ctx (ReaderT (W.Options -> String -> IO (Response BL.ByteString)) m) ()
routes = do
  get "healthz" $ text "ok"

1 个答案:

答案 0 :(得分:5)

我对您使用的库不熟悉,但是spockT的参数必须是多态的。您是说打电话 app的人来确定a是什么。但是spockT希望自己做出决定(可能以多种方式)。具体来说,

spockT :: MonadIO m => (forall a. m a -> IO a) -> SpockT m () -> IO Middleware

尝试将{-# language RankNTypes #-}添加到文件顶部,并将app的签名更改为

app
  :: (forall a. ReaderT (W.Options -> String -> IO (Response BL.ByteString)) IO a -> IO a)
  -> IO Middleware

更多说明:

spockT的类型如下

spockT :: MonadIO m => (forall a. m a -> IO a) -> ...

这是怎么回事? MonadIO类看起来像这样

class Monad m => MonadIO m where
  liftIO :: IO a -> m a

这意味着如果您拥有MonadIO m,那么您就拥有一个函数forall a. IO a -> m aspockT的函数参数看起来很像,但是却相反。如果m用某种“上下文”包装了IO动作,则该函数必须剥离该上下文,也许还要添加其他信息。