SMLでZipListを作る
本物のプログラマはHaskellを使うで紹介されているZipListを定義してみました。
この型を使うと、zipWithN をその場で組上げることが出来ます。(といっても最初にタプルのコンストラクタ渡すんですが)
型の定義
まず *モト* となる型を定義します。
signature ZIPLIST = sig type 'a list datatype 'a ZipList = ZipList of 'a list val getZipList : 'a ZipList -> 'a list end structure ZipList : ZIPLIST = struct local structure L = LazyList in type 'a list = 'a L.lazylist datatype 'a ZipList = ZipList of 'a list fun getZipList (ZipList xs) = xs end (* local *) end
隠蔽してもしょうがないのでsignatureに型を公開します。実体は後述する都合で遅延リストにしています。
SMLはHaskellのようにレコードのメンバが1個でも省略出来たりしないため、 getZipList は関数として追い出しました。コードの意味は変わっていません。
実装
次に ZipListで Applicative のプリミティブなインターフェースを実装します。
structure ZipListApplicative : APPLICATIVE_CORE = struct local structure L = LazyList fun id x = x in (* ---> SML/NJ workaround *) infix <*> (* SML/NJ workaround <--- *) open ZipList type 'a t = 'a ZipList.ZipList fun fmap f (ZipList xs) = ZipList (L.map f xs) fun pure x = ZipList (L.repeat x) fun (ZipList fs) <*> (ZipList xs) = ZipList (L.zipWith (fn (f,a)=> f a) fs xs) end (* local *) end
pure の実装で L.repeat (= [1,1,1,...]) を使うため ZipList の実体を遅延リストにしました。
Applicativeの要件である pure id <*> xs = xs を満たすためにこのようになっているようです。
強化版ZipListを作る
元のZipListとApplicativeを実装したZipListを一つのstructureに統合します。
structure ZipListApplicative = struct local structure XZipList = MkApplicative(ZipListApplicative) in open ZipList XZipList structure Open = XZipList.Open end end
もう単純作業でメンドクサイんですがどうしようもないです…。
動作確認
fun tup x y = (x,y); fun tup3 x y z = (x,y,z); (* タプルはレコードなのでコンストラクタというモノは無い *) infix --> fun s --> e = L.range s (SOME e); (* [s,s+1,s+2,...,e] の意味 *) - l.-!- (getZipList (tup <$> ZipList (0-->10) <*> ZipList (0-->10))); val it = [(0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10)] : (int * int) list - l.-!- (getZipList (pure tup3 <*> ZipList (0-->10) <*> ZipList (0-->10) <*> ZipList (0-->10))); val it = [(0,0,0),(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5),(6,6,6),(7,7,7),(8,8,8),(9,9,9),(10,10,10)] : (int * int) list
うまく動いてるようです。この調子で <*> で繋げていくと zipWithN が作り出せます。
ちなみにSML標準ライブラリにもzipWith相当の関数は定義されています。(当然正格リスト向けですが)
ListPair.map : ('a * 'b -> 'c) -> 'a list * 'b list -> 'c list
終わり
うーむあんまりウレシイ感じがしないなー。もし現実にzipWithが使いたくても手元にあるのは普通のリストだろうし。
まぁ単体で便利なものじゃないのかも知れない。
コードはcodeplexに置いてあります。 (ziplist.use)