我敢肯定我一定错过了什么.
我是Haskell的新手,曲线非常陡峭.我正在达到我的玩具项目的重点,我真的想要使用State monad来避免在任何地方传递一千个论点.我无法理解如何将IO中的State monad传递给纯代码.概念上像这样的东西(除了StateT而不是ExceptT):
import Control.Monad.Except
import Control.Monad.Identity
type PlayM = Except String
type PlayMIO = ExceptT String IO
puree :: String -> PlayM String
puree = return . ("bb"++)
impuree :: String -> PlayMIO String
impuree s = do
a <- return $ runIdentity $ runExceptT $ puree s
return $ "aa" ++ a
main = do
runExceptT $ impuree "foo"
putStrLn "hi"
Run Code Online (Sandbox Code Playgroud)
除了这不编译,给我这样的东西:
play.hs:15:20:
Couldn't match expected type ‘[Char]’
with actual type ‘Either String String’
In the second argument of ‘(++)’, namely ‘a’
In the second argument of ‘($)’, namely ‘"aa" ++ a’
Run Code Online (Sandbox Code Playgroud)
我现在明白为什么这不能编译,为什么类型就是它们,但对于我的生活,我无法理解如何做到这一点.这感觉不应该很难,但我对Haskell的直觉远非准确.
谢谢你的帮助!
-G
你很亲密 让我们按照类型孔的类型进行操作_:
impuree :: String -> PlayMIO String
impuree s = do
a <- _ . runIdentity . runExceptT $ puree s
return $ "aa" ++ a
Run Code Online (Sandbox Code Playgroud)
这告诉我们需要一个类型:
Test.hs:15:8:
Found hole ‘_’
with type: m0 (Either String String) -> ExceptT String IO [Char]
Where: ‘m0’ is an ambiguous type variable
Relevant bindings include
s :: String (bound at Test.hs:13:9)
impuree :: String -> PlayMIO String (bound at Test.hs:13:1)
In the first argument of ‘(.)’, namely ‘_’
In the expression: _ . return . runIdentity . runExceptT
In a stmt of a 'do' block:
a <- _ . return . runIdentity . runExceptT $ puree s
Run Code Online (Sandbox Code Playgroud)
现在,我们有什么事情,可以把m (Either e b)到ExceptT e m b:
ExceptT :: m (Either e b) -> ExceptT e m b
Run Code Online (Sandbox Code Playgroud)
应用它,我们得到正确的答案:
impuree :: String -> PlayMIO String
impuree s = do
a <- ExceptT . return . runIdentity . runExceptT $ puree s
return $ "aa" ++ a
Run Code Online (Sandbox Code Playgroud)
如果我们查看文档,我们可以看到模式ExceptT . f . runExceptT是用函数抽象的
mapExceptT :: (m (Either e a) -> n (Either e' b)) -> ExceptT e m a -> ExceptT e' n b
Run Code Online (Sandbox Code Playgroud)
在我们的例子中,m是Identity和n是IO.使用这个,我们得到:
impuree :: String -> PlayMIO String
impuree s = do
a <- mapExceptT (return . runIdentity) $ puree s
return $ "aa" ++ a
Run Code Online (Sandbox Code Playgroud)
这可能在这里有点过头了,但是值得注意的是,有一个叫做的包mmorph可以更好地处理monad态射(从一个monad到另一个monad的转换).这个包有一个generalize :: Monad m => Identity a -> m a我们可以使用的功能:
impuree :: String -> PlayMIO String
impuree s = do
a <- mapExceptT generalize $ puree s
return $ "aa" ++ a
Run Code Online (Sandbox Code Playgroud)
在我们谈论的时候mmorph,我们也可以使用更一般的形式:
impuree :: String -> PlayMIO String
impuree s = do
a <- hoist generalize $ puree s
return $ "aa" ++ a
Run Code Online (Sandbox Code Playgroud)
hoist推广mapExceptT到任何monad变换器类似的东西,你可以将monad态射应用于底层monad:
hoist :: (MFunctor t, Monad m) => (forall a. m a -> n a) -> t m b -> t n b
Run Code Online (Sandbox Code Playgroud)
在这里第一个正确答案之后的所有内容都只是奖励的东西,没有必要理解它以理解和使用解决方案.它可以在某些方面派上用场,这就是我加入它的原因.认识到monad态射的一般模式可以节省时间,但是如果没有额外的抽象级别,你总是可以更明确地做事.