简体   繁体   中英

Monad Transformer and applicative Maybe

Inside a do block of a ExceptT String IO ()

I have a function that produces a ReaderT like so:

type UDCEnv = (AWS.Env, Bool)

uploadVersionFilesToCaches :: S3.BucketName
                               -> FilePath
                               -> [GitRepoNameAndVersion]
                               -> ReaderT UDCEnv IO ()

I just so happen to have a Maybe FilePath so I create my ReaderT like so:

let maybeReader ::  Maybe (ReaderT UDCEnv IO ()) =
    uploadVersionFilesToCaches s3BucketName <$> maybeFilePath <*> Just gitRepoNamesAndVersions

I can even run the ReaderT like so:

let maybeIO :: Maybe (IO ()) = 
    runReaderT <$> maybeReader <*> Just (env, shouldIgnoreLocalCache, verbose)

Everything works fine as long as I use let expressions. As soon as I drop the let in the expression above to actually try to have expression evaluated Applicative gets types as ExceptT String IO FilePath instead of Maybe

The parts I am omitting are marked by ... :

f :: ... -> ExceptT String IO ()
f ... = do
   ... 
   runReaderT <$> maybeReader <*> Just (env, shouldIgnoreLocalCache, verbose) -- Error here
   undefined

Produces

Couldn't match type ‘IO ()’ with ‘()’
Expected type: ReaderT UDCEnv IO () -> UDCEnv -> ()
  Actual type: ReaderT UDCEnv IO () -> UDCEnv -> IO ()
In the first argument of ‘(<$>)’, namely ‘runReaderT’
In the first argument of ‘(<*>)’, namely
  ‘runReaderT
   <$>
     (uploadVersionFilesToCaches s3BucketName <$> maybeFilePath
      <*> Just gitRepoNamesAndVersions)’
/Users/blender/Code/Personal/Haskell/Rome-Public/src/Lib.hs: 82, 73

Couldn't match type ‘Maybe’ with ‘ExceptT String IO’
    Expected type: ExceptT String IO FilePath
      Actual type: Maybe FilePath
    In the second argument of ‘(<$>)’, namely ‘maybeFilePath’
    In the first argument of ‘(<*>)’, namely
      ‘uploadVersionFilesToCaches s3BucketName <$> maybeFilePath’

I think the first error is because I'm missing some liftIO somewhere.

However I have no idea what to do about the misunderstood Applicative.

I could case analysis on the Maybe of course instead of using Applicative but I would really prefer not to.

Edit: Oops, fixed a bug.

There seems to be a minor inconsistency in your question, because the do-block you provide contains a runReaderT ... expression that doesn't match the expression given in your error message.

However, ultimately the problem is this: in a do-block of type ma for some monad m , each plain expression (and each right-hand side of an x <- y expression) has to have type mb for some b . So, by using your runReaderT ... expression in a do-block of type ExceptT String IO () , you're forcing Haskell to type-check it as ExceptT String IO a for some a . However, it's a Maybe (IO ()) , so that type-checking will fail.

You'd get a similar error if you tried:

foo :: ExceptT String IO ()
foo = do Just (putStrLn "won't work")   -- has type Maybe (IO ())
         undefined

You need to decide how to adapt the runReaderT ... expression to the surrounding do-block. Two reasonable options are:

foo = do ...
         maybe (throwError "reader was Nothing!") liftIO
             $ runReaderT ...
         undefined

which will throw an ExceptT -style error if your maybeReader is Nothing or:

foo = do ...
         maybe (return ()) liftIO
             $ runReaderT ...
         undefined

which will do .. erm .. nothing in case of Nothing .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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