[英]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 的类型签名processCitations
有PandocMonad m
满足的要求; 您在仅提供Monad m
的上下文中调用它。
那是类型错误,如果不更改签名,您无法将其变为 go。 查看文档中关于PandocMonad
的内容,我希望您能看到PandocMonad
约束还需要Functor
、 Applicative
、 Monad
和MonadError 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
约束——如果他们选择一个已经是PandocMonad
的Monad
,他们可能已经在这样做了。
唯一的选择是render
具体使用一些特定PandocMonad
(如PandocIO
或PandocPure
)而不是使用多态性来支持任何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.