Search code examples
haskelltextfoldbytestringlenses

Making a single function work on lists, ByteStrings and Texts (and perhaps other similar representations)


I'm writing a function that does some searching in a sequence of arbitrary symbols. I'd like to make it generic enough so that it works on lists, Foldables as well on ByteStrings and Texts. Generalizing it to Foldable is simple. But how to include ByteStrings and Texts? Sure I could convert ByteString into a list and then call my function, but I'd lose all the advantages ByteStrings.

To have a concrete example let's say we want to make a histogram function:

import Control.Monad.State
import qualified Data.Foldable as F
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Word
import qualified Data.ByteString as B
import qualified Data.Text as T

type Histogram a = Map a Int

empty :: (Ord a) => Histogram a
empty = Map.empty

histogramStep :: (Ord a) => a -> Histogram a -> Histogram a
histogramStep k = Map.insertWith (+) k 1

histogram :: (Ord a, F.Foldable t) => t a -> Histogram a
histogram = F.foldl (flip histogramStep) empty

But since neither ByteString nor Text can be Foldable (it stores just Word8s/Chars, not arbitrary elements), I'm stuck with creating more functions that look exactly like the one before, just with a different type signatures:

histogramBS :: B.ByteString -> Histogram Word8
histogramBS = B.foldl (flip histogramStep) empty

histogramText :: T.Text -> Histogram Char
histogramText = T.foldl (flip histogramStep) empty

This is something one does not expect in a functional language like Haskell.

How to make it generic, to write histogram once and for all?


Solution

  • Your solution is pretty much what the ListLike package does. There's also the additional package listlike-instances which adds instances for Text and Vector.