在haskell程序中使用返回的EitherT
我试图在Haskell的一个项目中使用“引用解析”包,但在实际代码中使用EitherT时,我的头脑遇到了问题。我知道它们是单子变形金刚,我想我明白这意味着什么,但我似乎无法真正了解如何使用它们。代表我正在尝试做的事情的玩具示例如下:在haskell程序中使用返回的EitherT,haskell,monads,monad-transformers,either,io-monad,Haskell,Monads,Monad Transformers,Either,Io Monad,我试图在Haskell的一个项目中使用“引用解析”包,但在实际代码中使用EitherT时,我的头脑遇到了问题。我知道它们是单子变形金刚,我想我明白这意味着什么,但我似乎无法真正了解如何使用它们。代表我正在尝试做的事情的玩具示例如下: module Main where import Text.EditDistance import Text.CSL.Input.Identifier import Text.CSL.Reference import Control.Monad.Trans.Clas
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))
此处,resolve或
具有以下类型:
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
和runEitherT$resolve其中一个“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)
但是,这会产生以下错误:
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")
我不知道如何解决,也不知道如何解决
任何帮助都将不胜感激,特别是从使用角度而不是从实现角度来指导monad Transformer的教程
编辑:
为了反映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))
main=do
putStrLn“解析定义”
resRef do
putStrLn(“获取错误:++e)
右参考->执行
putStrLn(“添加了对数据库的引用:”++(show ref))
我现在得到的错误是:
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 { ... } } }
没有(MonadState s0 IO)的实例
因使用“ResolveOne”而产生
在“runEitherT”的第一个参数中,即
“(决议“doi:10.1145/2500365.2500595”)”
在“do”块的stmt中:
resRef do{…}}
我正在编辑我的问题和评论,因为这里的好代码格式比评论中的格式要简单得多。我认为问题在于,您试图在
resRef
上进行模式匹配,而您可能需要执行它并对结果进行模式匹配
所以你应该试试这个:
main = do
putStrLn "Resolving definition"
resRef <- runEitherT $ resolveEither "doi:10.1145/2500365.2500595"
case resRef of
Left e -> do
main=do
putStrLn“解析定义”
resRef do
好的,我想我已经找到了一个解决我最初问题的方法,那就是从函数resolvearth
(它提供的resolveDef
函数)中获取类型为IO(任意字符串引用)
的值
因此,resolve或
返回
(HasDatabase s, MonadIO m, MonadState s m) => String -> EitherT String m Reference
我们可以把它转换成一种
(HasDatabase s, MonadIO m, MonadState s m) => String -> m (Either String Reference)
使用runEitherT。解析其中一个
。当我问这个问题的时候,这就是我的目的。从那里,我尝试查看源代码,以了解库如何从函数resolvearth
中提取Reference
类型。该库使用以下功能:
resolve :: (MonadIO m, MonadState s m, HasDatabase s) => String -> m Reference
resolve = liftM (either (const emptyReference) id) . runEitherT . resolveEither
但是,我们希望保留eather,即删除liftM(eather(const-emptyReference)id)
然而,这让我们回到了开始的地方,所以我再次查看了源代码,并找出了如何使用这个函数。在库中,该函数用于以下函数,它将resolve
的输出类型从类型的值(MonadIO m、MonadState s m、HasDatabase s)=>m Reference
转换为IO Reference
类型之一:
resolveDef :: String -> IO Reference
resolveDef url = do
fn <- getDataFileName "default.db"
let go = withDatabaseFile fn $ resolve url
State.evalStateT go (def :: Database)
retEither s = do
fn <- getDataFileName "default.db"
let go = withDatabaseFile fn $ ( (runEitherT.resolveEither) s)
State.evalStateT go (Database Map.empty)
(我已将(def::Database)
替换为(Database Map.empty)
,因为def仅在引文解析
中内部定义)
然后,总体解决方案变成:
module Main where
import Text.EditDistance
import Text.CSL.Input.Identifier.Internal
import Text.CSL.Input.Identifier
import Text.CSL.Reference
import Control.Monad.Trans.Either
import Control.Monad.State as State
import qualified Data.Map.Strict as Map
main = do
putStrLn "Resolving definition"
resRef <- retEither "doi:10.1145/2500365.2500595"
case resRef of
Left e -> putStrLn ("Got error: "++ e)
Right ref -> putStrLn ("Added reference to database: "++ (show ref))
retEither s = do
fn <- getDataFileName "default.db"
let go = withDatabaseFile fn $ ((runEitherT.resolveEither) s)
State.evalStateT go (Database Map.empty)
modulemain其中
导入Text.EditDistance
导入Text.CSL.Input.Identifier.Internal
导入Text.CSL.Input.Identifier
导入Text.CSL.Reference
进口控制。单子。反式
导入控制.Monad.State作为状态
导入符合条件的Data.Map.Strict作为映射
main=do
putStrLn“解析定义”
resRef putStrLn(“获取错误:++e)
右ref->putStrLn(“添加了对数据库的引用:”++(show ref))
rets=do
fn您遇到了基于mtl
类的方法的一个缺点:可怕的类型错误。我认为想象一下普通的变形金刚
单声道变形金刚的情况会是什么样子会很有帮助。我希望这也能帮助你在monad变形金刚中站稳脚跟。(顺便说一句,你似乎已经理解了其中的大部分内容;我只是简单地解释一下。)
给出类型是一个很好的开始方式。以下是您所拥有的:
resolveEither :: (HasDatabase s,
MonadIO m,
MonadState s m)
=> String -> EitherT String m Reference
约束中隐藏了一种类型,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
我们所做的就是指定m
的类型,这样它就不再是变量了。只要满足类约束,我们就不需要这样做
现在更清楚的是,有两层monad变压器。由于我们的主要功能是在IO
monad中,我们希望最终得到一个类型为IO
的值,我们可以“运行”,例如使用来重新表述dfeuer的答案,您能试着用let resRef=runEitherT{-…-}
替换resRef吗,正如我的编辑中所反映的,我已经尝试过了,但仍然有类型错误。我想我知道为什么会这样,因为它从它“打开”了一层monad(即IO),对吗?因为某些原因,仍然会产生类似的错误:如果我将Main
修改为:Main=do resRef do
我会得到错误:`没有(MonadState s0 IO)的实例由于在“runEitherT”的第一个参数中使用了“resolveather”,即在“do”块的stmt中使用了“(resolveather”…):resRef关于样式:我将添加类型签名(至少到retother
)。另外,withDatabaseFile fn$((runEitherT.resolveover)s)
相当于withDatabaseFile fn。鲁尼瑟特。解析$s
。啊,当然-我