1

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.

tmpz
  • 368
  • 3
  • 12
  • Judging from the error, you are missing `return`, not `liftIO`. – arrowd Apr 14 '17 at 09:38
  • @arrowd could well be, but unfortunately that does not help with my applicative problem here. Maybe this is not the right approach to begin with? – tmpz Apr 14 '17 at 11:29
  • Can you include the code where the `runReaderT ...` expression actually appears? Most likely, it's being used in the do-block in a such a way that Haskell is trying to type it as `ExceptT String IO ()` instead of `Maybe (IO ())`. This would ultimately explain both errors, as `Maybe` isn't `ExceptT String IO`, and `()` isn't `IO ()`. It may be as simple as adding a standard Maybe-to-ExceptT interface. – K. A. Buhr Apr 20 '17 at 23:03
  • @K.A.Buhr it just appears in the middle of a `do` block in a `ExceptT String IO ()` see edit – tmpz Apr 21 '17 at 19:22

1 Answers1

2

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 m a for some monad m, each plain expression (and each right-hand side of an x <- y expression) has to have type m b 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.

K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71
  • You are right, I was completely confused by the error message and all those `T`s . Generally I find it quite difficult to compose monad transformers, `maybe` (wink wink) I'm doing it wrong :) – tmpz Apr 21 '17 at 21:43