代码之家  ›  专栏  ›  技术社区  ›  Jesse Shieh José Valim

无法将类型A与A1匹配,但类型看起来相同

  •  0
  • Jesse Shieh José Valim  · 技术社区  · 5 年前

    我在学习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 回复  |  直到 5 年前
        1
  •  4
  •   dfeuer    5 年前

    我对你正在使用的图书馆不熟悉,但是 spockT 必须是多态的。你是说那个人 电话 app 决定什么 a 是。但是 斯科克特 想要自己做出决定(可能有多种方式)。明确地,

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

    尝试添加 {-# language RankNTypes #-} 到文件的顶部并更改 应用程序

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

    再解释一下:

    类型 斯科克特 看起来像

    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 . 函数参数 斯科克特 看起来很像,但情况恰恰相反。如果 m 包装一个 IO 操作时使用某种“上下文”,那么函数必须除去该上下文,可能还需要添加其他信息。