hlian / linklater

A Haskell library for the Slack API (including real-time messaging!)
BSD 3-Clause "New" or "Revised" License
79 stars 11 forks source link

Trouble using 'say' function #9

Closed ggranberry closed 6 years ago

ggranberry commented 6 years ago

http://lpaste.net/360345

While writing a small slackbot using linklater I noticed that providing a Message and Config object to 'say' returned a value that violates functional dependencies. Can anyone provide any insights into what I'm doing wrong here?

hlian commented 6 years ago

@ggranberry try this!

(False,  m) -> do
  anEither <- say m config
  case anEither of
    Left e -> putStrLn ("an error occurred! " <> show e)
    Right _ -> return ""

The problem here is that typeclasses generate bad errors :(

You should think of say as having type IO (Either RequestError ()), which uses the typeclass instance MonadError e (Either e). The reason the very generic mtl type exists is that you can use it in deeper monad stacks where perhaps there are more layers between the IO and the Either RequestError.

Your code is forcing GHC to infer say m config as IO a. Now IO does have a MonadError instance, except it's MonadError IOException -- the weird mismatch between IOException and RequestError is what's causing the problem here.

Hope that makes sense. Keep me updated! Thanks for using the library.

ggranberry commented 6 years ago

@hlian Thanks for the quick and detailed reply! Your comment was incredibly helpful and I'm mostly seeing why it is breaking right now. Though some of the some of the mtl type stuff was a bit above me as this is my first attempt at a small Haskell project.

I went ahead and tried to implement your suggestion and got the same error as before with a few extra ones added. It looks like say isn't being evaluated as IO (Either RequestError ()) from what I'm seeing.


/Users/ranberry/haskell/scraper/src/Notification/SlackBot.hs:38:19: error:
    • Couldn't match type ‘IOException’
                     with ‘Network.Linklater.Types.RequestError’
        arising from a functional dependency between:
          constraint ‘mtl-2.2.1:Control.Monad.Error.Class.MonadError
                        Network.Linklater.Types.RequestError IO’
            arising from a use of ‘say’
          instance ‘mtl-2.2.1:Control.Monad.Error.Class.MonadError
                      IOException IO’
            at <no location info>
    • In a stmt of a 'do' block: anEither <- say m config
      In the expression:
        do { anEither <- say m config;
             case anEither of {
               Left e -> putStrLn ("an error occurred! " <> show e)
               Right _ -> return "" } }
      In a case alternative:
          (False, m)
            -> do { anEither <- say m config;
                    case anEither of {
                      Left e -> putStrLn ("an error occurred! " <> show e)
                      Right _ -> return "" } }

/Users/ranberry/haskell/scraper/src/Notification/SlackBot.hs:40:9: error:
    • Couldn't match expected type ‘()’ with actual type ‘Either a0 t0’
    • In the pattern: Left e
      In a case alternative:
          Left e -> putStrLn ("an error occurred! " <> show e)
      In a stmt of a 'do' block:
        case anEither of {
          Left e -> putStrLn ("an error occurred! " <> show e)
          Right _ -> return "" }

/Users/ranberry/haskell/scraper/src/Notification/SlackBot.hs:40:19: error:
    • Couldn't match type ‘()’ with ‘T.Text’
      Expected type: IO T.Text
        Actual type: IO ()
    • In the expression: putStrLn ("an error occurred! " <> show e)
      In a case alternative:
          Left e -> putStrLn ("an error occurred! " <> show e)
      In a stmt of a 'do' block:
        case anEither of {
          Left e -> putStrLn ("an error occurred! " <> show e)
          Right _ -> return "" }

/Users/ranberry/haskell/scraper/src/Notification/SlackBot.hs:41:9: error:
    • Couldn't match expected type ‘()’ with actual type ‘Either t1 t2’
    • In the pattern: Right _
      In a case alternative: Right _ -> return ""
      In a stmt of a 'do' block:
        case anEither of {
          Left e -> putStrLn ("an error occurred! " <> show e)
          Right _ -> return "" }```
hlian commented 6 years ago

@ggranberry silly me! i was missing a runExceptT call. i updated JPEGbot to illustrate the solution:

https://github.com/hlian/linklater/blob/master/examples/app/JointPhotographicExpertsGroupTonga.hs#L62

ggranberry commented 6 years ago

@hlian That did the trick, thanks!!