Text.CSV を使う

純粋型言語で副作用を許さないのなら、入出力はどうするの?

と思っていた時期がありました。
細かい理屈はさておき、Haskell でもファイルの読み書きは出来ることがわかったので、書いておくことにします。

使ったのは Text.CSV。最初に、ここで定義されている関数を hackage でチェックします。どうやらprintCSVがそれっぽいのですが、引数がCSV型のようです。

そこで CSV の定義を同じページで見てみると、

type CSV [Record]
type Record [Field]
type Field [String]

とたどっていけます。要するに CSVString ということのようです。数値を出力したい場合は、何か追加しないとだめだと分かりました。

これは後で対応するとして、まずはprintCSVの味見をしてみます。

writeFile "data.txt" $ printCSV [["a", "b", "c"], ["d", "e", "f"]]

出来た。


ついでに、棚上げしていた数値の出力についても。
printCSV が CSV しか受け取れないので、任意の型をとれる二重リストとして Rec をつくって見ましょう。必要なのは Rec の定義と、それを CSV に渡すために a にほどく関数です。

newtype Rec a =  Rec [[a]]
fromRec (Rec xss) = xss

Rec を type でなく newtype にしたのは、いずれ fmap とかも定義したいからです。ただ、二重リストの fmap をどのように定義すべきかは悩みどころでした。受け取った関数を全要素に適用するのか、一列目だけがよいのか......
今回は練習用として、全部の要素にたいして単純に適用するとしてみました。

instance Functor Rec where
  fmap f (Rec xss) = Rec $ map (map f) xss

Rec を二重リストにほどいて xss とし、受け取った関数 f をリストに適用出来るようにして(内側のmap)、さらにそれを二重リストに適用します(外側のmap)。
ここまでだと a 型がかえりますので、最後に Rec で包んで完成です。

とりあえずここまで。