Ada*_*ies 4 monads haskell monad-transformers either io-monad
我正在尝试在我正在使用的Haskell项目中使用"citation-resolve"包,但是我无法在实际代码中使用EitherT.我知道他们是monad变形金刚,我想我明白这意味着什么,但我似乎无法真正弄清楚如何使用它们.代表我正在尝试做的玩具示例如下:
module Main where
import Text.EditDistance
import Text.CSL.Input.Identifier
import Text.CSL.Reference
import Control.Monad.Trans.Class
import Control.Monad.Trans.Either
main = do
putStrLn "Resolving definition"
let resRef = runEitherT $ resolveEither "doi:10.1145/2500365.2500595"
case resRef of
Left e -> do
putStrLn ("Got error: "++ e)
Right ref -> do
putStrLn ("Added reference to database: "++ (show ref))
Run Code Online (Sandbox Code Playgroud)
这里resolveEither有类型:
resolveEither :: (HasDatabase s,
Control.Monad.IO.Class.MonadIO m,
mtl-2.1.3.1:Control.Monad.State.Class.MonadState s m)
=> String -> EitherT String m Reference
Run Code Online (Sandbox Code Playgroud)
并runEitherT $ resolveEither "ref"有类型:
runEitherT $ resolveEither "ref"
:: (HasDatabase s,
Control.Monad.IO.Class.MonadIO m,
mtl-2.1.3.1:Control.Monad.State.Class.MonadState s m)
=> m (Either String Reference)
Run Code Online (Sandbox Code Playgroud)
但是,这会产生以下错误:
Main.hs:10:34:
No instance for (Control.Monad.IO.Class.MonadIO (Either [Char]))
arising from a use of ‘resolveEither’
In the first argument of ‘runEitherT’, namely
‘(resolveEither "doi:10.1145/2500365.2500595")’
In the expression:
runEitherT (resolveEither "doi:10.1145/2500365.2500595")
In an equation for ‘resRef’:
resRef = runEitherT (resolveEither "doi:10.1145/2500365.2500595")
Run Code Online (Sandbox Code Playgroud)
我不知道如何解决或解决.
任何帮助将不胜感激,尤其是从使用角度处理monad变换器的教程的指针,而不是实现的.
编辑:
为了反映dfeuer和Christian对答案的评论,如果我将main更改为以下内容,我仍会收到错误:
main = do
putStrLn "Resolving definition"
resRef <- runEitherT (resolveEither "doi:10.1145/2500365.2500595")
case resRef of
Left e -> do
putStrLn ("Got error: "++ e)
Right ref -> do
putStrLn ("Added reference to database: "++ (show ref))
Run Code Online (Sandbox Code Playgroud)
我现在得到的错误是:
No instance for (MonadState s0 IO)
arising from a use of ‘resolveEither’
In the first argument of ‘runEitherT’, namely
‘(resolveEither "doi:10.1145/2500365.2500595")’
In a stmt of a 'do' block:
resRef <- runEitherT (resolveEither "doi:10.1145/2500365.2500595")
In the expression:
do { putStrLn "Resolving definition";
resRef <- runEitherT (resolveEither "doi:10.1145/2500365.2500595");
case resRef of {
Left e -> do { ... }
Right ref -> do { ... } } }
Run Code Online (Sandbox Code Playgroud)
我正在编辑我的问题以及评论,因为好的代码格式在这里比在评论中更容易.
我相信问题是你正在尝试模式匹配,resRef当你可能想要做的是执行它并模式匹配结果.
所以你应该试试这个:
main = do
putStrLn "Resolving definition"
resRef <- runEitherT $ resolveEither "doi:10.1145/2500365.2500595"
case resRef of
Left e -> do
Run Code Online (Sandbox Code Playgroud)
mtl您遇到了基于类的方法的缺点之一:令人生畏的类型错误。我认为想象一下transformers基于普通 monad 转换器的情况会是什么样子会很有帮助。我希望这也能帮助您熟悉 monad 转换器。(顺便说一下,看起来你已经理解了大部分内容;我只是将其拼写出来。)
给出类型是一个很好的开始方式。这就是你所拥有的:
resolveEither :: (HasDatabase s,
MonadIO m,
MonadState s m)
=> String -> EitherT String m Reference
Run Code Online (Sandbox Code Playgroud)
有一种隐藏在约束中的类型s,稍后它会回来咬你。粗略地说,这些约束表达了以下内容:s有一个数据库(无论在上下文中意味着什么);monad 或 monad 堆栈m位于IO其底部,并且 monad 堆栈中的某个位置m是StateT s一层。满足这些属性的最简单的 monad 堆栈m是HasDatabase s => StateT s IO. 所以我们可以这样写:
resolveEither' :: HasDatabase s
=> String -> EitherT String (StateT s IO) Reference
resolveEither' = resolveEither
Run Code Online (Sandbox Code Playgroud)
我们所做的只是指定 的类型,m因此它不再是变量。只要我们满足类约束,我们就不需要这样做。
现在更清楚的是,有两层 monad 转换器。由于我们的 main 函数位于IOmonad 中,因此我们希望最终得到一个类型的值IO,我们可以“运行”该值,例如<-在do符号中使用。我认为它是从外到内“剥离”单子变压器的层。(这就是“使用”单子变压器的归结。)
对于EitherT,有一个函数runEitherT :: EitherT e m a -> m (Either e a)。看看如何m从“内部”移动EitherT到“外部”?对我来说,这就是批判性的直觉观察。类似地StateT,有runStateT :: StateT s m a -> s -> m (a, s)。
(顺便说一句,两者都被定义为记录访问器,这是惯用的,但导致它们在 Haddock 中显示得有点奇怪,并且带有“错误”的类型签名;我花了一段时间才学会查看 Haddock 上的“构造函数”部分并在心里将EitherT e m a ->等添加到签名的前面。)
所以这加起来就是一个通用的解决方案,您基本上已经解决了:我们需要一个适当的类型值s(我将其称为s),然后我们可以使用flip runStateT s . runEitherT $ resolveEither "ref"它具有类型IO ((Either String Reference), s)。(假设我已经把类型记在脑子里了,但我可能没有。我flip第一次就忘记了。)然后我们可以进行模式匹配或使用fst来获取Either,这似乎是您真正想要的。
如果您希望我解释 GHC 给您的错误,我会很高兴。非正式地,它是说你没有“运行”或剥离所有 monad 转换器。更准确地说,它正在观察那IO不是类似的东西StateT s IO。通过使用runStateT和runEitherT,您可以强制或约束类型,以便最终满足类约束。当你犯了一些小错误时,这会让人感到困惑。
哦,关于编写解决方案的惯用方式:我不确定retEither这里是否惯用单独的函数,因为它看起来像是在干扰全局状态,即打开某种数据库文件。这取决于图书馆的习惯用法。
另外,通过使用evalStateT,您会在评估后隐式地丢弃状态,这可能是也可能不是一个坏主意。图书馆是否希望您重用数据库连接?
最后,你有一些额外的括号和一些缺失的类型签名;hlint会帮助你解决这些问题。
| 归档时间: |
|
| 查看次数: |
470 次 |
| 最近记录: |