OCamlのSet.Sにmap関数はない
最近研究でOCamlのSetやMapを多用するんですが、Set.Sの中にmap関数はないんですね。
タプルの特定要素を取り出したり情報を付与したりするときにSet->Set変換する必要があるんですが、いちいちfoldするのが面倒……
Listなんかにはmap関数があるのであってもよさそうなものですが、よく考えるとこの関数、型として表現が難しい(というかできない)のですね。
Setはファンクターなので、例えばint型のSetなら(作れないですが)
int Set
ではなく
module IntSet = Set.Make (struct type t = int let compare = compare end)
といった感じになります。
要素の型や比較関数をまとめてストラクチャーとしてファンクターに与えることで、内部の型が固定されたストラクチャーが返されるわけです。
んで、ファンクターの内部では集合自体の型はt、要素の型はeltで表されてるわけです。例えば
empty : tadd : elt -> t -> t
といった具合です。(OCamlマニュアルより)
ところがここでmap関数を考えると、
map : (elt -> 'a) -> t -> 'a set(?)
といった具合で、出力が’aを要素とするSetになるわけですが、当然ファンクターはそんな型を知らないので定義できません。
with type構文とか使えないかなーと一瞬考えましたが、あれはSet.Sに型を与えてシグネチャを作るだけでしたね。全然関係ないです。
無論、foldを使ってやれば似たようなことは簡単に出来ます。
ASet.t型のs1に射影子fを用いてBSet.t型のs2にする場合、
let s2 = ASet.fold (fun elt making -> BSet.add (f s2) making ) s1 BSet.empty
こんな感じでオッケーです。
fold万能説。
が、正直読みにくい。
出来れば、
let s2 = ASet.map f s1
みたいに書きたい。
ASetとBSetを渡して相互のmap関数を提供するMapperみたいなファンクタ作るという手もありますが、それもあまりスマートじゃない気がしますし……
以上、なんでSet.Sにmap関数がないか気になったので勝手に考察。
ちなみにMap.Sにはmap関数が用意されていますが、こちらはキー値に対応する値の方を射影するものなので、やっぱりキー値を射影子で別の型にしちゃう、みたいなのはできません。
ま、OCaml使いには当たり前の話なのかもしれませんが。
余談ですが、どうせ集合→集合の射影ができないなら、せめて同じ型内での射影
map : (elt -> elt) -> t -> t
みたいな関数用意しておいても良かったんじゃないかなーとか思ったり。
まぁ使いどころがどれだけあるかはわかりませんが。