4

I got problems with turning ByteString's into Text and vice versa. Here's the code:

{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Web.ClientSession

import Data.Text.Lazy (Text, toStrict, fromStrict)
import Data.Text.Lazy.Encoding (encodeUtf8, decodeUtf8)
import Data.ByteString (ByteString)

import Data.Monoid ((<>))

initCookies :: IO (Text -> ActionM ())
initCookies = do
  key <- getDefaultKey
  return $ setCookie key
  where
    setCookie k id = encryptIO k (encode id) 
      >>= (\x -> header "Set-Cookie" ("sid=" <> decode x <> ";"))

encode :: Text -> ByteString
encode = encodeUtf8 . toStrict

decode :: ByteString -> Text
decode = fromStrict . decodeUtf8

and error message:

foo.hs:16:35:
Couldn't match expected type `bytestring-0.10.0.2:Data.ByteString.Internal.ByteString'
with actual type `ByteString'
In the return type of a call of `encode'
In the second argument of `encryptIO', namely `(encode id)'
In the first argument of `(>>=)', namely `encryptIO k (encode id)'

foo.hs:17:18:
Couldn't match type `ActionM' with `IO'
Expected type: IO ()
Actual type: ActionM ()
In the return type of a call of `header'
In the expression: header "Set-Cookie" ("sid=" <> decode x <> ";")
In the second argument of `(>>=)', namely
`(\ x -> header "Set-Cookie" ("sid=" <> decode x <> ";"))'

foo.hs:17:56:
Couldn't match expected type `ByteString'
with actual type `bytestring-0.10.0.2:Data.ByteString.Internal.ByteString'
In the first argument of `decode', namely `x'
In the first argument of `(<>)', namely `decode x'
In the second argument of `(<>)', namely `decode x <> ";"'

So, my guess this error has something to do with what ClientSession actually use, in their source code they seem to use normal bytestring which should work with my implementation. Look here:

[..]
import qualified Data.ByteString as S
[..]
encryptIO :: Key -> S.ByteString -> IO S.ByteString
[..]

So why do I get all these errors? Thanks.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
klrr
  • 113
  • 1
  • 7
  • 2
    Do you have multiple versions of bytestring installed? – bennofs Sep 12 '13 at 14:32
  • Nope, only bytestring-0.10.2.0 as far as I know. Maybe Web.ClientSession is using a different one? – klrr Sep 12 '13 at 14:45
  • 1
    When `ghc` error message contains fully qualified type with package name, like `bytestring-0.10.0.2:Data.ByteString.Internal.ByteString`, then it is 99% that your package indirectly depends on two different versions of the package. @bennofs guess most likely is correct. Post output of `ghc-pkg list bytestring` – Yuras Sep 12 '13 at 15:32
  • 1
    E.g. see http://stackoverflow.com/questions/12576817/couldnt-match-expected-type-with-actual-type-error-when-using-codec-bmp/12577025#12577025 – Yuras Sep 12 '13 at 15:32
  • Yes, that was the problem, thanks! – klrr Sep 12 '13 at 18:00

2 Answers2

10

You were mixing Data.ByteString.ByteString and Data.ByteString.Lazy.ByteString. Because the type names are equal GHC can (and does) produce terrible error messages. I reworked it using explicit imports for ByteString and Text, hopefully it's a little more obvious now:

{-# LANGUAGE OverloadedStrings #-}

import Web.Scotty
import Web.ClientSession

import Control.Monad.Trans (liftIO)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import qualified Data.ByteString as B
import qualified Data.ByteString as BL

import Data.Monoid ((<>))

initCookies :: IO (TL.Text -> ActionM ())
initCookies = do
  key <- getDefaultKey
  return $ setCookie key
  where
    setCookie k id = liftIO (encryptIO k (encode id))
      >>= (\x -> header "Set-Cookie" ("sid=" <> decode x <> ";"))

encode :: TL.Text -> B.ByteString
encode = T.encodeUtf8 . TL.toStrict

decode :: B.ByteString -> TL.Text
decode = TL.fromStrict . T.decodeUtf8
Nathan Howell
  • 4,627
  • 1
  • 22
  • 30
  • The snippet imports only `Data.ByteString`. And the error complains about `ByteString` and `bytestring-0.10.0.2:Data.ByteString.Internal.ByteString`, both are strict byte strings. The lazy one is declared in `Data.ByteString.Lazy.Internal.ByteString`. What did I miss? – Yuras Sep 12 '13 at 17:24
  • @Yuras: The `Data.Text.Lazy.Encoding` module deals with `Data.ByteString.Lazy.ByteString`s not strict ones, the error message is misleading though. – Nathan Howell Sep 12 '13 at 17:37
  • You are referring to type error in `encode` and `decode`. But the author has error in `initCookies` (foo.hs:16:35) which is definitely about different `bytestring` versions. When he fix that issue, he will get the error you are referring too. I'm not saying that you are wrong, but the original issue is about dependency hell. – Yuras Sep 12 '13 at 18:48
  • @Yuras seems you're right, but it wouldn't have compiled anyways :^) – Nathan Howell Sep 12 '13 at 19:32
2

The Data.String.Conversions package abstracts that knowledge with a single cs conversion function, which version is called depending on the context of the call (i.e. input and expected type).

Titou
  • 968
  • 10
  • 16