Haskell has three dominant types of the implementations of strings; String, ByteString and Text(*1). Since String is the default literal of strings, most libraries only support String as strings, even though you can write ByteString or Text directly in a Haskell code with OverloadedStrings
pragma.
Haskell has IsString
class that means you can convert the type to String, using fromString
function. IsString
only requires the type to have the fromString
function.
-- in Data.String
class IsString a where
fromString :: String -> a
ToString
I implemented ToString
class that you can convert from the type to String by toString
function, oppositely to the IsString
class and its fromString
function.
{-# LANGUAGE TypeSynonymInstances #-}
import qualified Data.Text as T
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString as B
class ToString a where
toString :: a -> String
instance ToString String where
toString = id
instance ToString T.Text where
toString = T.unpack
instance ToString B.ByteString where
toString = C.unpack
You can use them like the following way.
{-# LANGUAGE OverloadedStrings #-}
main = do
putStrLn $ toString ("hello world!" :: String)
putStrLn $ toString ("hello world!" :: T.Text)
putStrLn $ toString ("hello world!" :: B.ByteString)
It might remind you about show :: a -> String
. show
and the toString
I defined here have the same type but the purpose and the behaviour are different. show
is to give human-readable and machine-parsable String represantation while toString
is just to convert from a type which is an implementation of the concept of strings to String.
If you are familiar with Ruby, this may make sense; show
in Haskell is inspect
in Ruby while toString
in Haskell is to_str
in Ruby. Note that that's not to_s
whom Integer or some other non-string classes also have.
Use case
An example is Network.HTTP.urlEncode function; the function is String -> String
but you may want to use it for Text values.
import qualified Data.Text as T
import qualified Network.HTTP as H
urlEncodeText :: T.Text -> T.Text
urlEncodeText = T.pack . H.urlEncode . T.unpack
You can define Text-specific version of urlEncode
easily. You can define ByteString-specific version as well in the same way. But you cannot have general one.
It's turn to use the ToString
we saw here.
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeSynonymInstances #-}
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString as B
import qualified Network.HTTP as H
import Data.String (fromString, IsString)
main = do
putStrLn $ urlEncode ("hello world!" :: String)
T.putStrLn $ urlEncode ("hello world!" :: T.Text)
B.putStrLn $ urlEncode ("hello world!" :: B.ByteString)
class ToString a where
toString :: a -> String
instance ToString String where
toString = id
instance ToString T.Text where
toString = T.unpack
instance ToString B.ByteString where
toString = C.unpack
urlEncode :: IsString a => ToString a => a -> a
urlEncode = fromString . H.urlEncode . toString
This urlEncode
function defined here can use String, ByteString and Text just by the single function! I just combined IsString and ToString there :)
No comments:
Post a Comment