[web-devel] Re: ANN: Salvia-1.0.0

Gregory Collins greg at gregorycollins.net
Tue Mar 23 17:22:05 EDT 2010

Michael Snoyman <michael at snoyman.com> writes:

> * "Request and response headers should have case-insensitive match for
> the Eq instance." I'm thinking that this is the correct approach to
> take, and would like input on it.  (Thanks Gregory.

Here's our (dead simple) code for this:

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}

module Data.CIByteString
 ( CIByteString
 , toCI
 , unCI
 ) where

-- for IsString instance
import           Data.ByteString.Char8 ()
import           Data.ByteString (ByteString)
import           Data.ByteString.Internal (c2w, w2c)
import qualified Data.ByteString as S
import           Data.Char
import           Data.String

-- | A case-insensitive newtype wrapper for ByteString
data CIByteString = CIByteString { unCI        :: !ByteString
                                 , _lowercased :: !ByteString }

toCI :: ByteString -> CIByteString
toCI s = CIByteString s t
    t = lowercase s

instance Show CIByteString where
    show (CIByteString s _) = show s

lowercase :: ByteString -> ByteString
lowercase = S.map (c2w . toLower . w2c)

instance Eq CIByteString where
    (CIByteString _ a) == (CIByteString _ b) = a == b
    (CIByteString _ a) /= (CIByteString _ b) = a /= b

instance Ord CIByteString where
    (CIByteString _ a) <= (CIByteString _ b) = a <= b

instance IsString CIByteString where
    fromString = toCI . fromString

Note that we store the downcased version in the datatype, otherwise
Eq/Ord has to perform the downcase a zillion times, only to throw it
away afterwards. Note also that we ignore any encoding issues -- we can
get away w/ this because HTTP header names are constrained by the
protocol to be ASCII-only.

This datatype also has an "IsString" instance so you can use string
literals with "{-# LANGUAGE OverloadedStrings #-}" turned on.

Gregory Collins <greg at gregorycollins.net>

More information about the web-devel mailing list