|
1 | | -module Data.List.Types where |
| 1 | +module Data.List.Types |
| 2 | + ( List(..) |
| 3 | + , (:) |
| 4 | + , NonEmptyList(..) |
| 5 | + ) where |
2 | 6 |
|
3 | 7 | import Prelude |
4 | 8 |
|
@@ -67,7 +71,47 @@ instance monoidList :: Monoid (List a) where |
67 | 71 | mempty = Nil |
68 | 72 |
|
69 | 73 | instance functorList :: Functor List where |
70 | | - map f = foldr (\x acc -> f x : acc) Nil |
| 74 | + map = listMap |
| 75 | + |
| 76 | +-- chunked list Functor inspired by OCaml |
| 77 | +-- https://discuss.ocaml.org/t/a-new-list-map-that-is-both-stack-safe-and-fast/865 |
| 78 | +-- chunk sizes determined through experimentation |
| 79 | +listMap :: forall a b. (a -> b) -> List a -> List b |
| 80 | +listMap f = startUnrolledMap unrollLimit |
| 81 | + where |
| 82 | + -- iterate the unrolled map up to 1000 times, |
| 83 | + -- which hits up to 5000 elements |
| 84 | + unrollLimit = 1000 |
| 85 | + |
| 86 | + startUnrolledMap :: Int -> List a -> List b |
| 87 | + startUnrolledMap 0 (x : xs) = f x : chunkedRevMap xs |
| 88 | + startUnrolledMap n (x1 : x2 : x3 : x4 : x5 : xs) = |
| 89 | + f x1 : f x2 : f x3 : f x4 : f x5 : startUnrolledMap (n - 1) xs |
| 90 | + startUnrolledMap n (x1 : x2 : x3 : x4 : xs) = |
| 91 | + f x1 : f x2 : f x3 : f x4 : startUnrolledMap (n - 1) xs |
| 92 | + startUnrolledMap n (x1 : x2 : x3 : xs) = |
| 93 | + f x1 : f x2 : f x3 : startUnrolledMap (n - 1) xs |
| 94 | + startUnrolledMap n (x1 : x2 : xs) = |
| 95 | + f x1 : f x2 : startUnrolledMap (n - 1) xs |
| 96 | + startUnrolledMap n (x : xs) = f x : startUnrolledMap (n - 1) xs |
| 97 | + |
| 98 | + startUnrolledMap _ Nil = Nil |
| 99 | + |
| 100 | + chunkedRevMap :: List a -> List b |
| 101 | + chunkedRevMap = go Nil |
| 102 | + where |
| 103 | + go :: List (List a) -> List a -> List b |
| 104 | + go chunksAcc chunk@(x1 : x2 : x3 : x4 : x5 : xs) = |
| 105 | + go (chunk : chunksAcc) xs |
| 106 | + go chunksAcc finalChunk = |
| 107 | + reverseUnrolledMap chunksAcc $ startUnrolledMap 0 finalChunk |
| 108 | + |
| 109 | + reverseUnrolledMap :: List (List a) -> List b -> List b |
| 110 | + reverseUnrolledMap ((x1 : x2 : x3 : _) : cs) acc = |
| 111 | + reverseUnrolledMap cs (f x1 : f x2 : f x3 : acc) |
| 112 | + -- if we pattern match on Nil, we need a Partial constraint, |
| 113 | + -- which kills TCO |
| 114 | + reverseUnrolledMap _ acc = acc |
71 | 115 |
|
72 | 116 | instance functorWithIndexList :: FunctorWithIndex Int List where |
73 | 117 | mapWithIndex f = foldrWithIndex (\i x acc -> f i x : acc) Nil |
|
0 commit comments