Version 2 (modified by gregorycollins, 15 months ago)


Proposal: Add the case-insensitive package to the Haskell Platform

This is a proposal for the 'case-insensitive' package to be included in the next major release of the Haskell platform.

Everyone is invited to review this proposal, following the standard procedure for proposing and reviewing packages.

Review comments should be sent to the libraries mailing list before Feb 10, which is the discussion deadline.


Proposal author: Gregory Collins

Package maintainer: Bas van Dijk

The following individuals contributed to the review process:

  • Gregory Collins
  • ...


The case-insensitive package provides a case-insensitive wrapper around string types. (ByteString?, Text, String, etc.)

Documentation and tarball from the hackage page:

Development repo:


Used whenever you need a case-insensitive comparison of two strings, usually as the key in an associative container. Does the right thing, i.e. stores a cached copy of the downcased string for comparisons only, while retaining the original string's casing. No current solution in the platform. Both the Snap Framework and Yesod have standardized on this package as the natural way of representing map keys in web programming, which are often case-insensitive (e.g.: http headers). In wide use: at the time of this writing has 48 reverse dependencies on Hackage.

Introduction to the API

The API is structured as follows:

  • Data.CaseInsensitive: contains the "CI s" datatype, which has instances for Eq, Ord, Read, Show, IsString?, Monoid, etc. Also provides a "FoldCase?" typeclass:
    -- | Class of string-like types that support folding cases.
    -- /Note/: In some languages, case conversion is a locale- and context-dependent
    -- operation. The @foldCase@ method is /not/ intended to be locale sensitive.
    -- Programs that require locale sensitivity should use appropriate versions of
    -- the case mapping functions from the @text-icu@ package:
    -- <>
    class FoldCase s where foldCase ∷ s → s
  • Functions to convert between strings and CI strings:
    -- | Make the given string-like value case insensitive.
    mk ∷ FoldCase s ⇒ s → CI s
    -- | And the two accessor functions are exposed.
    data CI s = CI { original   ∷ !s -- ^ Retrieve the original string-like value.
                   , foldedCase ∷ !s -- ^ Retrieve the case folded string-like value.
                                     --   (Also see 'foldCase').

Design decisions

There's only one real decision: store the case-folded version or not? I think keeping it is best: it trades space for making the second and subsequent comparisons faster. Searching a Map could involve comparing the lookup key several times, so overall I think this is a win.

Open issues