如何修复 Haskell 中的"could not deduce"错误?



所以我试图让Pandoc在呈现文件时处理我的引用。目前我在Rib的分叉中使用这个功能,看起来像这样:

-- | 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添加到该函数的上下文中,但这显然意味着我也必须将其添加到该链中其他所有函数的上下文。这不可能是对的,是吗?我怎么能只运行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

但这告诉我,它无法匹配";潘多克"可能是因为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的函数,但没有成功。

好吧,第一步我复制了您代码的最小但完整的版本(完整意味着:我可以编译它,并实际得到您报告的错误,而不仅仅是所有丢失的导入和其他定义)。在未来,如果你这样做并把它放在问题中,你更有可能得到回应。我不需要你链接的所有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,我想我必须运行操作?

据我所见,您在实现中编写了正确的代码。但是,确实必须更新函数的类型签名。CCD_ 10具有CCD_ 11保持的要求;您在仅提供Monad m的上下文中调用它。

这就是类型错误,如果不更改签名,就无法使其消失。看看文档对PandocMonad的描述,我希望您能看到PandocMonad约束还需要FunctorApplicativeMonadMonadError PandocError实例,以及它自己的19个方法。如果您将函数的类型保留为render :: Monad m => Pandoc -> HtmlT m (),那么您就声称可以调用processCitations,它需要所有这些,并且您可以使用任何monad来调用它。你说render可以适用于任何monad,甚至Identity,它实际上根本不提供任何基本类型的功能!这是一个你显然无法兑现的承诺。你需要改变承诺(也就是类型签名),而不是寻找能为你实现承诺的神奇功能。

如果您将类型签名更改为此,那么代码只需编译:

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

这就是";将CCD_ 23添加到CCD_;意味着与PandocMonad Identity无关。(实际上,将其添加到上下文中的字面意思是(Monad m, PandocMonad m),但PandocMonad m已经暗示了Monad m,所以我们可以简化)。

这也是GHC为render推断的类型,如果您删除类型签名而不更改其他内容;它不会产生相同的错误。可能您在调用render的另一个函数中遇到了相同的错误。

恐怕您需要更改任何调用render且仅提供Monad m的函数的类型签名,理由与上述相同。然而,这将非常快(很难"从头开始重写整个系统"),因为编译器会准确地向您指出需要更改的函数,因为它会检测到丢失的约束,正如您在这里看到的那样。那些刚刚从其类型传递Monad m约束的函数,也只需要在其类型中添加6个字母Pandoc;那些实际上满足Monad约束的函数(通常只有其中一个)现在需要满足PandocMonad约束——如果他们选择了一个已经是PandocMonadMonad,他们可能已经在做了。

唯一的选择是render具体使用某些特定的PandocMonad(如PandocIOPandocPure),而不是使用多态性来支持任何PandocMonad,或者具体使用可以变成PandocMonad的具体成员的monad(例如,PandocIO aExceptT PandocError (StateT CommonState IO) a的一个新类型包装器,所以理论上你可以将其转换为PandocIO a来运行processCitations)。这些将是更糟糕的选择,而且仍然不会让你摆脱更新render的呼叫者,所以我甚至不会考虑它们。

强类型编程的一个现实是,当您在调用堆栈中更深入地更改某些内容,使其需要更多的环境(添加参数或约束、修复过去通用的类型等)时,您通常还需要更改调用方。这种情况在其他语言中也会发生(例如,在Java中,如果您将一个方法切换为需要比过去更具体的子类,则必须更新所有接受更通用类并将其传递下去的调用方)。即使在像Python这样的动态语言中,当你进行类似的更改时,你可能不需要更新任何类型签名,但你通常需要使用全面的测试套件和/或痛苦的调试时间来找到所有需要更新旧代码以满足新需求的地方。

最新更新