OverloadedLists拡張を有効化すると、通常のリストリテラルの型が[a]からforall t. IsList t => tになります。

IsList型クラスはGHC.Extsで定義されており、以下のような定義になっております。

class IsList l where
  type family Item l :: *
  fromList :: [Item l] -> l
  fromListN :: Int -> [Item l] -> l
  toList :: l -> [Item l]

定義を読んでいただければわかるかと思いますが、Item lが要素の型であり、Item lのリストとの相互変換が可能なものをIsListのインスタンスとして定義できます。


実際に使ってみましょう。とりあえず試しに、Data.Map.Map, Data.Set.Set, Data.Array.ArrayをIsListのインスタンスにしてみました。

{-# LANGUAGE
    OverloadedLists
  , FlexibleInstances
  , TypeFamilies #-}

import GHC.Exts (IsList(..))

import qualified Data.Array as A
import qualified Data.Map as M
import qualified Data.Set as S

instance Ord k => IsList (M.Map k v) where
  type Item (M.Map k v) = (k, v)
  fromList = M.fromList
  toList = M.toList

instance Ord a => IsList (S.Set a) where
  type Item (S.Set a) = a
  fromList = S.fromList
  toList = S.toList

instance IsList (A.Array Int a) where
  type Item (A.Array Int a) = a
  fromList xs = A.listArray (0, m) xs
    where m = length xs - 1
  toList = A.elems

実際に使用してみると以下のようになります。

>>> :l OverloadedList.hs -- 先ほどのソースコード
[1 of 1] Compiling Main             ( OverloadedList.hs, interpreted )
Ok, modules loaded: Main.
>>> :set -XOverloadedLists
>>> :t [1, 2, 3]
[1, 2, 3] :: (Num (Item l), IsList l) => l
>>> S.member 3 [1, 2, 3, 4, 5] -- Setとして使用してみる
True
>>> M.lookup "Taro" [("Taro", "Tokyo"), ("Jiro", "Osaka"), ("Saburo", "Sapporo")] -- Mapとして使用してみる
Just "Tokyo"
>>> (["hoge", "fuga", "foo", "bar"] :: A.Array Int String) A.! 3 -- Arrayとして使用してみる
"bar"

Arrayの場合、添字の型が判定できないので型注釈が必要となりますが、大きなプログラムではあまり問題にならないでしょう。