簡體   English   中英

如何修復 Haskell 中的“無法推斷”錯誤?

[英]How can I fix a "could not deduce" error in Haskell?

所以我試圖讓 Pandoc 在呈現文件時處理我的引文。 目前我在我的 Rib 分支中使用這個 function ,它看起來像這樣:

-- | Render a Pandoc document to HTML
render :: Monad m => Pandoc -> HtmlT m ()
render doc =
  either error id $ first show $ runExcept $ do
    runPure'
    $ fmap toHtmlRaw
    $ writeHtml5String writerSettings doc

由於processCitations具有類型簽名PandocMonad m => Pandoc -> m Pandoc我想我必須運行該操作? 所以我試過這個:

-- | Render a Pandoc document to HTML
render :: Monad m => Pandoc -> HtmlT m ()
render doc = do 
  processed <- processCitations doc
  either error id $ first show $ runExcept $ do
    runPure'
    $ fmap toHtmlRaw
    $ writeHtml5String writerSettings processed

但是我收到一個我不習慣看到的錯誤:

    • Could not deduce (PandocMonad m)
        arising from a use of ‘processCitations’
      from the context: Monad m
        bound by the type signature for:
                   render :: forall (m :: * -> *). Monad m => Pandoc -> HtmlT m ()
        at src/Pandoc.hs:70:1-41
      Possible fix:
        add (PandocMonad m) to the context of
          the type signature for:
            render :: forall (m :: * -> *). Monad m => Pandoc -> HtmlT m ()

我不確定我明白這意味着什么。 我已經嘗試將PandocMonad Identity添加到該 function 的上下文中,但這顯然意味着我必須將它也添加到該鏈中所有其他 function 的上下文中。 那不可能是對的,不是嗎? 我怎樣才能運行processCitations而不必從頭開始重寫我的整個系統?

編輯:根據下面的評論,我也試過這個:

-- | Render a Pandoc document to HTML
render :: Monad m => Pandoc -> HtmlT m ()
render doc =
  either error id $ first show $ runExcept $ do
    let processed = processCitations doc :: Pandoc
    runPure' $ fmap toHtmlRaw $ writeHtml5String writerSettings processed

但這告訴我它無法匹配“Pandoc”類型。 可能是因為processCitations返回一個m Pandoc 與此情況類似:

-- | Render a Pandoc document to HTML
render :: Monad m => Pandoc -> HtmlT m ()
render doc =
  either error id $ first show $ runExcept $ do
    processed <- processCitations doc :: Pandoc
    runPure' $ fmap toHtmlRaw $ writeHtml5String writerSettings processed

我嘗試使用 Hoogle 獲取類型為m Pandoc -> Pandoc的 function,但沒有成功。

好的,第一步我復制了一個最小但完整的代碼版本(完整意味着:我可以編譯它並實際得到你報告的錯誤,而不僅僅是所有丟失的導入和其他定義)。 將來,如果您這樣做並將其放入問題中,您將更有可能得到答復。 我不需要你鏈接的文件的所有 156 行,但我確實需要比你在問題中實際給我們的更多。 如果你沒有碰巧在我懶得這樣做的時候抓住我,我會繼續前進而不會進一步研究這個問題。

這里是:

{-# LANGUAGE FlexibleContexts #-}

import Control.Monad.Except

import Data.Bifunctor

import Lucid

import Text.Pandoc
import Text.Pandoc.Citeproc

-- | Render a Pandoc document to HTML
render :: Monad m => Pandoc -> HtmlT m ()
render doc = do
  processed <- processCitations doc
  either error id $ first show $ runExcept $ do
    runPure'
    $ fmap toHtmlRaw
    $ writeHtml5String writerSettings processed


runPure' :: MonadError PandocError m => PandocPure a -> m a
runPure' = liftEither . runPure

writerSettings :: WriterOptions
writerSettings = def {writerExtensions = pandocExtensions}

現在,你在這里完全正確:

由於processCitations具有類型簽名PandocMonad m => Pandoc -> m Pandoc我想我必須運行該操作?

據我所知,您在實現中編寫了正確的代碼。 但是,必須更新 function 的類型簽名processCitationsPandocMonad m滿足的要求; 您在僅提供Monad m的上下文中調用它。

那是類型錯誤,如果不更改簽名,您無法將其變為 go。 查看文檔中關於PandocMonad的內容,我希望您能看到PandocMonad約束還需要FunctorApplicativeMonadMonadError PandocError實例,以及它自己的 19 種方法。 如果您將 function 的類型保留為render:: Monad m => Pandoc -> HtmlT m () ,則您聲稱可以調用processCitations ,它需要所有這些,並且您可以使用任何 monad . 您說render可以適用於任何 monad,甚至Identity ,它實際上並沒有在基本類型上提供任何功能。 那是一個 promise 你顯然不能保留,你需要改變 promise (也就是類型簽名)。 不要尋找可以為您實現 promise 的神奇功能。

如果將類型簽名更改為此,則代碼將簡單地編譯:

render :: PandocMonad m => Pandoc -> HtmlT m ()

這就是“add (PandocMonad m) to the context of the type signature for: render ”的意思,與PandocMonad Identity無關。 (好吧,實際上將它添加到上下文中字面意思是(Monad m, PandocMonad m) ,但是PandocMonad m已經暗示了Monad m所以我們可以簡化)。

如果您刪除類型簽名並且不更改任何其他內容,它也是 GHC 為render推斷的類型; 它不會產生相同的錯誤。 可能您在另一個調用render的 function 處遇到了相同的錯誤。

恐怕您需要通過與上述相同的推理來更改調用render且僅提供Monad m的任何 function 的類型簽名。 然而,這將非常快(幾乎不會“從頭開始重寫整個系統”),因為編譯器會准確指出需要更改的函數,因為它會檢測到缺少的約束,正如您在此處看到的那樣。 那些只是從它們的類型傳遞Monad m約束的函數,也只需要在它們的類型中添加 6 個字母Pandoc 那些實際上滿足Monad約束的函數(通常只有其中一個)現在需要滿足PandocMonad約束——如果他們選擇一個已經是PandocMonadMonad ,他們可能已經在這樣做了。

唯一的選擇是render具體使用一些特定PandocMonad (如PandocIOPandocPure )而不是使用多態性來支持任何PandocMonad ,或者具體使用 monad 你可以變成PandocMonad的具體成員(例如PandocIO a是一個新類型包裝器對於ExceptT PandocError (StateT CommonState IO) a ,所以理論上你可以把它變成PandocIO a只是為了運行processCitations )。 這些將是更糟糕的選擇,並且仍然不會讓您擺脫更新render的調用者,所以我什至不會考慮它們。

強類型編程的一個現實是,當您更改調用堆棧中更深的內容以使其需要更多環境(添加 arguments 或約束、修復過去通用的類型等)時,您通常還需要更改來電者。 這也發生在其他語言中(例如,在 Java 中,如果您將方法切換為需要比以前更具體的子類,則必須更新所有接受更通用的 class 並將其向下傳遞的調用方)。 即使在像 Python 這樣的動態語言中,當你進行類似的更改時,你可能不需要更新任何類型簽名,但你通常需要使用你的綜合測試套件和/或痛苦的調試時間來找到舊代碼所在的所有地方需要更新以滿足新的要求。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM