ジンジャー研究室

長めのつぶやき。難しいことは書きません。

Elm 用の CSV デコーダーを作った

Elm 界隈では構文解析するライブラリを「パーサー(Parser)」、それを Elm の型に落とし込むものを「デコーダー(Decoder)」と呼び分けることが多い。パーサーは既にあって(lovasoa/elm-csv)、デコーダーで良いものがなかったので作った。

CsvDecode - elm-csv-decode 1.0.0

使い方

elm-tools/parser インスパイアのパイプライン式。

-- CSV の各行をこの User 型にしたい
type alias User =
    { id : String
    , name : String
    , age : Int
    , mail : Maybe String
    }


-- デコーダー Decoder User を作る
userDecoder : Decoder User
userDecoder =
    succeed User
        |= field "id"
        |= field "name"
        |= int (field "age")
        |= optional (field "mail")


-- デコードしたい CSV を用意
source : String
source =
    """
id,name,age,mail
1,John Smith,20,john@example.com
2,Jane Smith,19,
"""


-- 実行
> CsvDecode.run userDecoder source
Ok
    [ { id = "1", name = "John Smith", age = 20, mail = Just "john@example.com" }
    , { id = "2", name = "Jane Smith", age = 19, mail = Nothing }
    ]

API 一覧

型を見るだけで使い方が本当に分かるのか実験。

-- Types
type Decoder a
type alias Options = { separator : String, noHeader : Bool }

-- Primitives
succeed : a -> Decoder a
fail : String -> Decoder a
field : String -> Decoder String
index : Int -> Decoder String
fieldsAfter : String -> Decoder (Dict String String)
fieldsBetween : String -> String -> Decoder (Dict String String)

-- Convertion
int : Decoder String -> Decoder Int
float : Decoder String -> Decoder Float
string : Decoder String -> Decoder String
optional : Decoder a -> Decoder (Maybe a)

-- Transform
(|=) : Decoder (a -> b) -> Decoder a -> Decoder b
map : (a -> b) -> Decoder a -> Decoder b
andThen : (a -> Decoder b) -> Decoder a -> Decoder b

-- Run
run : Decoder a -> String -> Result String (List a)
runWithOptions : Options -> Decoder a -> String -> Result String (List a)
runAll : Decoder a -> String -> (List a, List String)
runAllWithOptions : Options -> Decoder a -> String -> (List a, List String)
defaultOptions : Options

分からないと思うのでドキュメント読んでください。

苦労した点

空文字の扱い。要するに foo,,bar の2列目。特に optional を使った時に、数値だと Nothing なのに文字列だと Just "" になるのは微妙なので、デフォルトで空文字は null 扱いにして string と指定した場合のみ値が存在することにした。

ソース

割ときれいに書けたかもしれない。

elm-test は最近のアップデートでトップレベルに Test 型の関数を置くだけで勝手に実行してくれるようになった。便利。