[英]haskell, how to deal with mismatched types - monads
I can't deal with following problem: 我无法处理以下问题:
I have some function, that return foo :: a -> b -> ErrorT String IO Int
我有一些函数,返回foo :: a -> b -> ErrorT String IO Int
I know that it returns IO (Either String Int)
. 我知道它会返回IO (Either String Int)
。
Nevertheless, I have also more complex function, that returns: 不过,我还有更复杂的函数,它返回:
bar :: a -> b -> StateT Char (ReaderT Char (ErrorT String IO)) Int
This function bar
calls function foo
. 此功能bar
调用函数foo
。 I am going to to following thing: 我要去做以下事情:
If foo
called throwError
bar
also throws the same error. 如果foo
调用throwError
bar
也将抛出相同的错误。 If foo
returns Int
bar
also returns Int
. 如果foo
返回Int
bar
也将返回Int
。
However, it is possible due to mismatch types. 但是,可能由于类型不匹配而导致。
I dont know how to do this in elegant way. 我不知道如何以优雅的方式做到这一点。 I think that my order of transformator monad is not ok. 我认为我的变换单子命令不正确。
Something like this should work: 这样的事情应该起作用:
bar x y = do
-- some code
z <- lift $ lift $ foo x y
-- some more code
return z
What I typically do is using what I've dubbed Transformer Monad Classes : 我通常做的是使用我称为Transformer Monad类的内容 :
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.Reader
import Control.Monad.State
inner :: ReaderT Char IO Int
inner = do
a <- ask
lift $ print a
return 5
inner' :: (MonadReader Char m, MonadIO m) => m Int
inner' = do
a <- ask
liftIO $ print a
return 5
outer :: StateT Char (ReaderT Char IO) Int
outer = do
a <- get
b <- lift $ inner -- need to lift
c <- inner' -- no need to lift
lift . lift $ print "need to lift twice to get to IO"
return 5
main = runReaderT (runStateT outer 'b') 'a'
Let's break it down; 让我们分解一下; the first inner
function has a concrete type you need to directly lift to in order to use it. 第一个inner
函数具有具体类型,您需要直接将其提升才能使用它。 However, if you parametrize it leaving only capabilities in the signature, you can skip the lifting, as long as it's unambiguous in the stack where to get this capability. 但是,如果将其参数化,则仅在签名中保留功能 ,只要它在获取此功能的堆栈中是明确的,就可以跳过提升。 In this case it's clear, because StateT Char (ReaderT Char IO) Int
has exactly one instance for MonadIO
(from IO
) and exactly one instance of MonadReader Char
(from ReaderT ... Char
). 在这种情况下很明显,因为StateT Char (ReaderT Char IO) Int
具有MonadIO
一个实例(来自IO
)和MonadReader Char
一个实例(来自ReaderT ... Char
)。
Now, it doesn't matter how many lifts you need to make, as long as the instance is clear! 现在,只要实例清晰即可,您需要进行多少次举升就无关紧要了 ! Consider: 考虑:
outer' :: ErrorT String (StateT Char (ReaderT Char IO)) Int
outer' = do
a <- inner' -- still no need to lift!
b <- lift . lift $ inner -- need to double lift in this case
return 5
The last thing that might not be obvious is that the signature of outer
could be expressed in a generic way as well: 最后一件可能并不明显的事情是, outer
的签名也可以用一种通用的方式表示:
outer :: (MonadState Char m, MonadReader Char m, MonadIO m) => m Int
And it would still work without any lifts (well sans liftIO
for IO operations, because functions like print
are defined in terms of IO
, not MonadIO m
. In comparison, functions like ask
and get
are defined in terms of the respective MonadX
class which allows us to skip the lifting). 它仍然没有任何升降机工作(以及SANS liftIO
的IO操作,因为喜欢功能print
中的术语定义IO
,不MonadIO m
。相比较而言,像功能ask
,并get
在相应的条款规定MonadX
类,允许我们跳过此举)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.