在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

我试图在Haskell的一个项目中使用“引用解析”包,但在实际代码中使用EitherT时,我的头脑遇到了问题。我知道它们是单子变形金刚,我想我明白这意味着什么,但我似乎无法真正了解如何使用它们。代表我正在尝试做的事情的玩具示例如下:

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
。啊,当然-我