简体   繁体   English

haskell,如何处理不匹配的类型-monads

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM