我正在学习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"
答案 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 a
。 spockT
的函数参数看起来很像,但是却相反。如果m
用某种“上下文”包装了IO
动作,则该函数必须剥离该上下文,也许还要添加其他信息。