In my Haskell Program I need to work with Strings and ByteStrings:
import Data.ByteString.Lazy as BS (ByteString)
import Data.ByteString.Char8 as C8 (pack)
import Data.Char (chr)
stringToBS :: String -> ByteString
stringToBS str = C8.pack str
bsToString :: BS.ByteString -> String
bsToString bs = map (chr . fromEnum) . BS.unpack $ bs
bsToString works fine, but stringToBS results with following error at compiling:
Couldn't match expected type ‘ByteString’
with actual type ‘Data.ByteString.Internal.ByteString’
NB: ‘ByteString’ is defined in ‘Data.ByteString.Lazy.Internal’
‘Data.ByteString.Internal.ByteString’
is defined in ‘Data.ByteString.Internal’
In the expression: pack str
In an equation for ‘stringToBS’: stringToBS str = pack str
But I need to let it be ByteString from Data.ByteString.Lazy as BS (ByteString) for further working functions in my code.
Any idea how to solve my problem?
You are working with both strict ByteStrings and lazy ByteStrings which are two different types.
This import:
import Data.ByteString.Lazy as BS (ByteString)
makes ByteString refer the lazy ByteStrings, so the type signature of your stringToBS doesn't match it's definition:
stringToBS :: String -> ByteString -- refers to lazy ByteStrings
stringToBS str = C8.pack str -- refers to strict ByteStrings
I think it would be a better idea to use import qualified like this:
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Char8 as BS
and use BS.ByteString and LBS.ByteString to refer to strict / lazy ByteStrings.
You can convert between lazy and non-lazy versions using fromStrict, and toStrict (both functions are in the lazy bytestring module).
Related
I vaguely recall a textual conversion table for Haskell textual type, but I can't find this anymore.
How can convert to do the conversion for Data.Text.Internal.Text -> Data.ByteString.Lazy.ByteString? Hoogle unfortunately shows no results.
The elements of a Text are Char, which are unicode code points. The elements of a ByteString are eight bit words. So in order to convert Text to ByteString we will need to choose an encoding. This is almost always UTF-8, hence encodeUtf8:
% ghci
> :m +Data.Text
> :m +Data.Text.Encoding
> :m +Data.ByteString.Lazy
> let t = Data.ByteString.Lazy.fromStrict (encodeUtf8 (Data.Text.pack "Qi Baishi 齊白石"))
> t
"Qi Baishi \233\189\138\231\153\189\231\159\179"
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
f1 :: T.Text -> TL.Text
f1 = TL.fromStrict
f2 :: TL.Text -> BL.ByteString
f2 = TL.encodeUtf8
f3 :: T.Text -> BL.ByteString
f3 = f2 . f1
I'm trying to pass a random string (which happens to be a number) "4176730.5" to SHA in Haskell to get a larger random string like "2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881".
I have this code to generate a random number and cast it to a string
num <- randomIO :: IO Float
let x = C.pack (show (num*10000000))
print x
but when I pass it to SHA with
let a = sha256 x
I get the error
Couldn't match expected type ‘Data.ByteString.Lazy.Internal.ByteString’
with actual type ‘C.ByteString’
I've tried casting my number to C.ByteString, but I think there are two types of Bytestring, according to the Haskell compiler.
The full code is:
import Data.Digest.Pure.SHA
import System.Random
import qualified Data.ByteString.Char8 as C
main :: IO ()
main = do
num <- randomIO :: IO Float
let x = C.pack (show (num*10000000))
print x
let a = sha256 x
b = hmacSha256 "key" "some test message"
mapM_ print [showDigest a, showDigest b]
Seeing as how there are apparently two types of Bytestring, and I'm casting to the wrong one, how do I cast my random string correctly?
Further to #Cubic's answer below if I replace
import qualified Data.ByteString.Char8 as C
with
import qualified Data.ByteString.Lazy as C
I just get these errors
Couldn't match type ‘Char’ with ‘GHC.Word.Word8’
Expected type: [GHC.Word.Word8]
Actual type: String
and
Couldn't match expected type ‘C.ByteString’
with actual type ‘[Char]’
The issue is that a ByteString is a sequence of bytes, while a String is a sequence of chars. There are many ways to turn chars into bytes, so you need to specify which encoding you want. Most likely, you want an ASCII or UTF8 encoding. If so, you can use this solution below, which converts strings into "UTF8 bytes" as needed.
import Data.Digest.Pure.SHA
import System.Random
import qualified Data.ByteString.Lazy as C
import qualified Data.ByteString.Lazy.UTF8 as U
main :: IO ()
main = do
num <- randomIO :: IO Float
let x = U.fromString (show (num*10000000))
print x
let a = sha256 x
b = hmacSha256 (U.fromString "key") (U.fromString "some test message")
mapM_ print [showDigest a, showDigest b]
You need Data.ByteString.Lazy, not Data.ByteString.Char8.
In general, you almost never want Data.ByteString.Char8.
Just use the lazy bytestrings as #leftaroundabout mentioned. Your try didn't work because you want to pack from Strings, so you need to import the .Char8 module to achieve that:
import Data.ByteString.Lazy.Char8 as C
This is a followup to this earlier question. I have a conduit source (from Network.HTTP.Conduit) which is strict ByteString. I will like to recombine them into larger chunks (to send over network to another client, after another encoding and conversion to lazy bytestring). I wrote chunksOfAtLeast conduit, derived from the answer in above question which seems to work pretty well. I am wondering if there is any further scope for improving it performance-wise.
import Data.Conduit as C
import Control.Monad.IO.Class
import Control.Monad
import Data.Conduit.Combinators as CC
import Data.Conduit.List as CL
import Data.ByteString.Lazy as LBS hiding (putStrLn)
import Data.ByteString as BS hiding (putStrLn)
chunksOfAtLeast :: Monad m => Int -> Conduit BS.ByteString m BS.ByteString
chunksOfAtLeast chunkSize =
loop
where
loop = do
bs <- takeE chunkSize =$= ((BS.concat . ($ [])) <$> CL.fold (\front next -> front . (next:)) id)
unless (BS.null bs) $ do
yield bs
loop
main = do
yieldMany ["hello", "there", "world!"] $$ chunksOfAtLeast 8 =$ CL.mapM_ Prelude.print
Getting optimal performance is always a case of trying something and benchmarking it, so I can't tell you with certainty that I'm offering you something more efficient. That said, combining smaller chunks of data into larger chunks is a primary goal of builders, so leveraging them may be more efficient. Here's an example:
{-# LANGUAGE OverloadedStrings #-}
import Conduit
import Data.ByteString (ByteString)
import Data.ByteString.Builder (byteString)
import Data.Conduit.ByteString.Builder
bufferChunks :: Conduit ByteString IO ByteString
bufferChunks = mapC byteString =$= builderToByteString
main :: IO ()
main = yieldMany ["hello", "there", "world!"] $$ bufferChunks =$ mapM_C print
I have the following code (which is supposed to parse a very trivial { "url": "http://some.url.here/" } hash):
import Control.Applicative
import qualified Data.ByteString as B
import Data.ByteString.Char8 (pack)
import Data.Aeson ()
import Data.Aeson.Types
newtype SetNextUrl = SetNextUrl B.ByteString
instance FromJSON SetNextUrl where
parseJSON (Object v) = SetNextUrl <$>
(pack <$> v .: "url" )
Now notice that I'm hinting that "url" is of type String by using pack... This of course will cause some conversion overhead: from the input ByteString to a [Char] and back....
Question: How can I ask Aeson to interpret the "url" field as a ByteString?
aeson uses Text internally for string values, so if you use Data.Text.Encoding.encodeUtf8 you won't have the Text -> String -> ByteString conversion, it'll just go straight from Text -> ByteString (which iirc is fairly cheap)
How could I write a function with a definition something like...
readBinaryFile :: Filename -> IO Data.ByteString
I've got the functional parts of Haskell down, but the type system and monads still make my head hurt. Can someone write and explain how that function works to me?
import Data.ByteString.Lazy
readFile fp
easy as pie man. Knock off the lazy if you don't want the string to be lazy.
import Data.ByteString.Lazy as BS
import Data.Word
import Data.Bits
fileToWordList :: String -> IO [Word8]
fileToWordList fp = do
contents <- BS.readFile fp
return $ unpack contents
readBinaryFile :: Filename -> IO Data.ByteString
This is simply the Data.ByteString.readFile function, which you should never have to write, since it is in the bytestring package.