generateの罠

vectorパッケージのData.Vectorにはgenerateという関数がある。

generate :: Int -> (Int -> a) -> Vector a

型から全てを知ることはできないが、だいたい想像通りgenerate n f[f 0, f 1, f 2, ...f (n - 1)]からなるVectorを生成する。しかし、これは要素を評価はしない。生成されるのはあくまでサンクのVectorだ。

Prelude > import Data.Vector as V
Prelude V> V.length $ V.generate 5 (const undefined)
5

vectorは速くて正格そうなイメージがあるが、ボックス化される方に関して、基本的に正格性は最小限なので注意しよう。どう工夫してもgenerateだけで正格なVectorは作れないので、generateMを使おう。

Prelude V> V.length $ runIdentity $ V.generateM 5 $ const $ pure $! undefined
5

Identityではダメなようだ…だが、継続モナドContを使うとうまくいく。

cont :: ((a -> r) -> r) -> Cont r a
runCont :: Cont r a -> (a -> r) -> r
V.length $ flip runCont id $ V.generateM 5 $ \_ -> cont $ \k -> k $! undefined
*** Exception: Prelude.undefined

継続最高。