Search code examples
haskellalgebraic-data-types

Retaining list-ness operations for a data type


I want to be able to define a custom data type as opposed to using a type alias to ensure that proper values are being passed around, below is a sketch of how that might look,

module Example (fromList) where

import Data.Ord (comparing, Down(..))
import Data.List (sort)

data DictEntry = DictEntry (String, Integer) deriving (Show, Eq)

instance Ord DictEntry where
  (DictEntry (word1, freq1)) `compare` (DictEntry (word2, freq2))
    | freq1 == freq2 = word1 `compare` word2
    | otherwise = comparing Down freq1 freq2

data Dictionary = Dictionary [DictEntry] deriving (Show)

fromList :: [(String, Integer)] -> Dictionary
fromList l = Dictionary $ sort $ map DictEntry l

However, I'd also like to retain the "list-ness" of the underlying type without having to unwrap and re-wrap [DictEntry], and without having to define utility functions such as head :: Dictionary -> DictEntry and tail :: Dictionary -> Dictionary. Is that possible? Is there some type class that I could define an instance of or a language extension that enables this?


Solution

  • Never use head and avoid using tail, for lists or else. These are unsafe and can always easily be replaced with pattern matching.

    But yes, there is a typeclass that supports list-like operations, or rather multiple classes. The simplest of these is Monoid, which just implements concatenation and empty-initialisation. Foldable, allows you to deconstruct containers as if they were lists. Traversable additionally allows you to assemble them again as you go over the data.

    The latter two won't quite work with Dictionary because it's not parametric on the contained type. You can circumvent that by switching to the “monomorphic version”.

    However, I frankly don't think you should do any of this – just use the standard Map type to store key-value associative data, instead of rolling your own dictionary type.