classy-preludeというhaskellパッケージの紹介です.

これは2014年の記事で, 今はyesodのデフォルトなどでは真逆の方法, 別名を付けないという解決方法が行われています.

というわけで, Data.HashMap などを使うときには, 一般的には import qualified で別名を付ける必要が出てきます.

例えば lookup 関数は単なる関数で, baseの Data.List では, lookup :: Eq a => a -> [(a, b)] -> Maybe b と定義されています. unordered-containersの Data.HashMap.Lazy では lookup :: (Eq k, Hashable k) => k -> HashMap k v -> Maybe v と定義されています.

lookup , insert , length , member , update などですね. データ構造に対する典型的な関数たちは多く被っています.

haskellでは多くのデータ構造に対する関数がほぼ同じ意図を持っているのにも関わらず, 違うモジュールで違う型で定義されています.

classy-preludeで解決

抽象的には lookup や member は同じことのはずで, データ構造が異なる場合に違う関数を使わなければいけないというのはスマートではありません.

lookup がクラスの関数なら, 違うデータ構造でも同じ関数を使えたはずです.

まさにそれを行ってくれるのがclassy-preludeです.

classy-preludeはクラス化された関数群を提供してくれます. 例えば, 先ほど述べた lookup はclassy-preludeがre-exportするmono-traversableのData.Containersに定義され, その定義元クラスである IsMap は List と HashMap 両方に instance を提供してくれています. classy-preludeのサポートするデータ構造(実際に実装されているのはmono-traversableですが)を使っている限りは, import で別名を付ける必要はありません. classy-preludeがラップしてくれます.

ただ, 完全にbaseなどの内容をそのままクラス化できたわけではなく, fromList などは setFromList と mapFromList に分けられていたりします.

classy-preludeを使う注意点として, head などの例外を容易に発生させる可能性のある関数はそのまま移植されておらず, NonNull に限られていることが挙げられます. headMay :: MonoFoldable mono => mono -> Maybe (Element mono) や readMay :: (Element c ~ Char, MonoFoldable c, Read a) => c -> Maybe a などの Maybe 付きにその能力を去勢された関数があるので, それを使いましょう. headEx :: MonoFoldable mono => mono -> Element mono というそのままの関数が存在しますが, 危険なので推奨はしません.

また, classy-preludeはデータ構造への関数だけではなく, putStrLn などのIO周りの被りが多く存在する関数も1つに統一していて, baseと違って Text と ByteString を使っています. String を使っている既存プロジェクトを移行させるときは少し手を加える必要があるかもしれません.

classy-preludeを使うにはもともとのpreludeの import を省く必要があるので, 以下のようにプラグマをつけて import しましょう.

{-# LANGUAGE NoImplicitPrelude #-} import ClassyPrelude