動機
私が利用している国立国会図書館サーチのメタデータは、国立国会図書館ダブリンコアメタデータ記述(DC-NDL)に準拠している。 今回は書籍のページ数の情報が必要になったため、DC-NDL の該当するフィールドを調べてみることにした。
<dcterms:extent>120p ; 22cm</dcterms:extent>
ページ数は dcterms:extent にリテラルとして収められているらしい。
API 緩衝層
一般に国立国会図書館サーチのような API を挟む場合、バックエンド(またはフロントエンド)と API エンドポイントとの間に、自分のアプリケーションで用いるために API から返されるデータを加工する緩衝層が必要になる。
今回の場合は title
と creator
、 description
、 publisher
、そして pages
のデータが必要であり、以下の for 文で構造体に情報を代入している(プログラム言語は Go 言語)。
title
等の pages
以外のフィールドの値は加工なしで用いることができるが、pages
の値だけは dcterms:extent
から抽出する必要がある。
つまり、 title
等の値に対する変換は恒等写像である。
for _, prop := range bibPropKeys { value := bibProp(prop) switch prop { case "extent": re := regexp.MustCompile(`\d+`).FindString(value) i, err := strconv.ParseInt(re, 10, 64) if err != nil { return nil, err } bibliographicInfo.Pages = int(i) case "title": bibliographicInfo.Title = value case "creator": bibliographicInfo.Creator = value case "description": bibliographicInfo.Description = value case "publisher": bibliographicInfo.Publisher = value } }
ただし bibPropKeys
は文字列 title
等が入っている配列、bibliographicInfo
は構造体である。
しかし私はこれを手続き的であり、データと処理が密結合であると思う1。
そのため、この switch 文を 「propKey
」と「変換を行う関数」からなる対(map)に抽出したい。
しかしそれは Go だと難しい。
関数が第一級関数とはいえ、pages
以外のフィールドの値は String 型である一方、pages
の値だけは Int 型であり、Go には合併型が存在しないため、map の型は map[string]func(string) interface{}
となってしまう。つまり、型が曖昧になってしまう。
関数型言語である Elixir であれば、関数 Function.identity/1
を用いて次のように変換写像を明瞭に分離できる。
conversion_map = %{ "extent" => fn value -> re = Regex.run(~r/\d+/, value) |> List.first() case Integer.parse(re) do {i, _} -> %{pages: i} _error -> {:error, "Failed to parse integer for extent"} end end, "title" => Function.identity(), "creator" => Function.identity(), "description" => Function.identity(), "publisher" => Function.identity() } Enum.reduce(bib_prop_keys, %{}, fn prop, acc -> conversion_fn = Map.get(conversion_map, prop, fn _ -> fn _ -> %{} end end) value = bib_prop(prop) updated_info = conversion_fn.(value) Map.merge(acc, updated_info) end)
つまり、API 緩衝層で行う変換の取捨選択を Map で明瞭に表現することができるのである。
加工せずとも使えるフィールドは identity/1
として表現し、加工が必要なフィールドには適宜適切な変換関数を与える。
このようにして、API 緩衝層が行うべき「変換」を手続き的ではなく、(空間的な)「データとして」与えるのである。
まとめ
今回の件を通して、Go は純粋関数型言語ではなく、関数型プログラミングには使いにくい言語であることが分かった。
- 関数型プログラミング信者であるため。↩