Skip to content

Commit c4875cc

Browse files
Martin Bidlingmaierpaf31
authored andcommitted
Add -WithIndex classes (#72)
* Add -WithIndex classes This adds `FunctorWithIndex`, `FoldableWithIndex` and `TraversableWithIndex` and some combinators. Implementations are mostly copied from the versions without index with minor adaptions. Similarly for the documentation. I think the corresponding haskell package does not state all laws for these type classes, so there are TODO's in the doc strings of the three classes. `StateL` and `StateR` in TraversableWithIndex.purs are exact copies of the versions in Traversable.purs because they are not exported there. Maybe it would be better to merge TraversableWithIndex.purs into Traversable.purs and similarly for Foldable. The Foldable and Traverse instances of Array could then be along the lines of ```purescript traverse f = itraverse (const f)` ``` so that the foreign js imports for the unindexed version could be removed. `FunctorWithIndex`'s `imap` collides with the `imap` of purescript-invariant. Haskell calls the latter `invmap`. I don't know whether that's a problem. This does not yet include the `-1WithIndex` classes, i.e. generalizations of `Foldable1` and `Traversable1` in `Semigroup/`. I guess it would make sense to provide them here as well. * Fix export lists * Add missing semicolon in js * Remove foreign implementation of array's itraverse * Move `Accum` and State to separate module * Remove foreign implementation of array's folds * Settle with incomplete laws The modified `Foldable` and `Traversable` laws that must hold for their -WithIndex versions are missing in the docs. But because the actual unmodified laws for `Foldable` and `Traversable` themselves are not here either I guess it shouldn't be a problem. * Add some test for FoldableWithIndex No tests for - ifoldM - itraverse_ - ifor_ - iall - iany - all instances except for Array These are not tested for Foldable either. * Add test for Array's TraversableWithIndex instance * Fix name: Add mising i- prefix * Change index naming scheme `i-` -> `-WithIndex` * Revert accidentally renamed Bifoldable functions
1 parent 223584f commit c4875cc

File tree

8 files changed

+664
-41
lines changed

8 files changed

+664
-41
lines changed

src/Data/FoldableWithIndex.purs

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
module Data.FoldableWithIndex
2+
( class FoldableWithIndex, foldrWithIndex, foldlWithIndex, foldMapWithIndex
3+
, foldrWithIndexDefault
4+
, foldlWithIndexDefault
5+
, foldMapWithIndexDefaultR
6+
, foldMapWithIndexDefaultL
7+
, foldWithIndexM
8+
, traverseWithIndex_
9+
, forWithIndex_
10+
, surroundMapWithIndex
11+
, allWithIndex
12+
, anyWithIndex
13+
, findWithIndex
14+
) where
15+
16+
import Prelude
17+
18+
import Data.Foldable (class Foldable, foldMap, foldl, foldr)
19+
import Data.FunctorWithIndex (mapWithIndex)
20+
import Data.Maybe (Maybe(..))
21+
import Data.Maybe.First (First)
22+
import Data.Maybe.Last (Last)
23+
import Data.Monoid (class Monoid, mempty)
24+
import Data.Monoid.Additive (Additive)
25+
import Data.Monoid.Conj (Conj(..))
26+
import Data.Monoid.Disj (Disj(..))
27+
import Data.Monoid.Dual (Dual(..))
28+
import Data.Monoid.Endo (Endo(..))
29+
import Data.Monoid.Multiplicative (Multiplicative)
30+
import Data.Newtype (unwrap)
31+
32+
-- | A `Foldable` with an additional index.
33+
-- | A `FoldableWithIndex` instance must be compatible with its `Foldable`
34+
-- | instance
35+
-- | ```purescript
36+
-- | foldr f = foldrWithIndex (const f)
37+
-- | foldl f = foldlWithIndex (const f)
38+
-- | foldMap f = foldMapWithIndex (const f)
39+
-- | ```
40+
-- |
41+
-- | Default implementations are provided by the following functions:
42+
-- |
43+
-- | - `foldrWithIndexDefault`
44+
-- | - `foldlWithIndexDefault`
45+
-- | - `foldMapWithIndexDefaultR`
46+
-- | - `foldMapWithIndexDefaultL`
47+
-- |
48+
-- | Note: some combinations of the default implementations are unsafe to
49+
-- | use together - causing a non-terminating mutually recursive cycle.
50+
-- | These combinations are documented per function.
51+
class Foldable f <= FoldableWithIndex i f | f -> i where
52+
foldrWithIndex :: forall a b. (i -> a -> b -> b) -> b -> f a -> b
53+
foldlWithIndex :: forall a b. (i -> b -> a -> b) -> b -> f a -> b
54+
foldMapWithIndex :: forall a m. Monoid m => (i -> a -> m) -> f a -> m
55+
56+
-- | A default implementation of `foldrWithIndex` using `foldMapWithIndex`.
57+
-- |
58+
-- | Note: when defining a `FoldableWithIndex` instance, this function is
59+
-- | unsafe to use in combination with `foldMapWithIndexDefaultR`.
60+
foldrWithIndexDefault
61+
:: forall i f a b
62+
. FoldableWithIndex i f
63+
=> (i -> a -> b -> b)
64+
-> b
65+
-> f a
66+
-> b
67+
foldrWithIndexDefault c u xs = unwrap (foldMapWithIndex (\i -> Endo <<< c i) xs) u
68+
69+
-- | A default implementation of `foldlWithIndex` using `foldMapWithIndex`.
70+
-- |
71+
-- | Note: when defining a `FoldableWithIndex` instance, this function is
72+
-- | unsafe to use in combination with `foldMapWithIndexDefaultL`.
73+
foldlWithIndexDefault
74+
:: forall i f a b
75+
. FoldableWithIndex i f
76+
=> (i -> b -> a -> b)
77+
-> b
78+
-> f a
79+
-> b
80+
foldlWithIndexDefault c u xs = unwrap (unwrap (foldMapWithIndex (\i -> Dual <<< Endo <<< flip (c i)) xs)) u
81+
82+
-- | A default implementation of `foldMapWithIndex` using `foldrWithIndex`.
83+
-- |
84+
-- | Note: when defining a `FoldableWithIndex` instance, this function is
85+
-- | unsafe to use in combination with `foldrWithIndexDefault`.
86+
foldMapWithIndexDefaultR
87+
:: forall i f a m
88+
. FoldableWithIndex i f
89+
=> Monoid m
90+
=> (i -> a -> m)
91+
-> f a
92+
-> m
93+
foldMapWithIndexDefaultR f = foldrWithIndex (\i x acc -> f i x <> acc) mempty
94+
95+
-- | A default implementation of `foldMapWithIndex` using `foldlWithIndex`.
96+
-- |
97+
-- | Note: when defining a `FoldableWithIndex` instance, this function is
98+
-- | unsafe to use in combination with `foldlWithIndexDefault`.
99+
foldMapWithIndexDefaultL
100+
:: forall i f a m
101+
. FoldableWithIndex i f
102+
=> Monoid m
103+
=> (i -> a -> m)
104+
-> f a
105+
-> m
106+
foldMapWithIndexDefaultL f = foldlWithIndex (\i acc x -> acc <> f i x) mempty
107+
108+
data Tuple a b = Tuple a b
109+
110+
instance foldableWithIndexArray :: FoldableWithIndex Int Array where
111+
foldrWithIndex f z = foldr (\(Tuple i x) y -> f i x y) z <<< mapWithIndex Tuple
112+
foldlWithIndex f z = foldl (\y (Tuple i x) -> f i y x) z <<< mapWithIndex Tuple
113+
foldMapWithIndex = foldMapWithIndexDefaultR
114+
115+
instance foldableWithIndexMaybe :: FoldableWithIndex Unit Maybe where
116+
foldrWithIndex f = foldr $ f unit
117+
foldlWithIndex f = foldl $ f unit
118+
foldMapWithIndex f = foldMap $ f unit
119+
120+
instance foldableWithIndexFirst :: FoldableWithIndex Unit First where
121+
foldrWithIndex f = foldr $ f unit
122+
foldlWithIndex f = foldl $ f unit
123+
foldMapWithIndex f = foldMap $ f unit
124+
125+
instance foldableWithIndexLast :: FoldableWithIndex Unit Last where
126+
foldrWithIndex f = foldr $ f unit
127+
foldlWithIndex f = foldl $ f unit
128+
foldMapWithIndex f = foldMap $ f unit
129+
130+
instance foldableWithIndexAdditive :: FoldableWithIndex Unit Additive where
131+
foldrWithIndex f = foldr $ f unit
132+
foldlWithIndex f = foldl $ f unit
133+
foldMapWithIndex f = foldMap $ f unit
134+
135+
instance foldableWithIndexDual :: FoldableWithIndex Unit Dual where
136+
foldrWithIndex f = foldr $ f unit
137+
foldlWithIndex f = foldl $ f unit
138+
foldMapWithIndex f = foldMap $ f unit
139+
140+
instance foldableWithIndexDisj :: FoldableWithIndex Unit Disj where
141+
foldrWithIndex f = foldr $ f unit
142+
foldlWithIndex f = foldl $ f unit
143+
foldMapWithIndex f = foldMap $ f unit
144+
145+
instance foldableWithIndexConj :: FoldableWithIndex Unit Conj where
146+
foldrWithIndex f = foldr $ f unit
147+
foldlWithIndex f = foldl $ f unit
148+
foldMapWithIndex f = foldMap $ f unit
149+
150+
instance foldableWithIndexMultiplicative :: FoldableWithIndex Unit Multiplicative where
151+
foldrWithIndex f = foldr $ f unit
152+
foldlWithIndex f = foldl $ f unit
153+
foldMapWithIndex f = foldMap $ f unit
154+
155+
156+
-- | Similar to 'foldlWithIndex', but the result is encapsulated in a monad.
157+
-- |
158+
-- | Note: this function is not generally stack-safe, e.g., for monads which
159+
-- | build up thunks a la `Eff`.
160+
foldWithIndexM
161+
:: forall i f m a b
162+
. FoldableWithIndex i f
163+
=> Monad m
164+
=> (i -> a -> b -> m a)
165+
-> a
166+
-> f b
167+
-> m a
168+
foldWithIndexM f a0 = foldlWithIndex (\i ma b -> ma >>= flip (f i) b) (pure a0)
169+
170+
-- | Traverse a data structure with access to the index, performing some
171+
-- | effects encoded by an `Applicative` functor at each value, ignoring the
172+
-- | final result.
173+
-- |
174+
-- | For example:
175+
-- |
176+
-- | ```purescript
177+
-- | > traverseWithIndex_ (curry logShow) ["a", "b", "c"]
178+
-- | (Tuple 0 "a")
179+
-- | (Tuple 1 "b")
180+
-- | (Tuple 2 "c")
181+
-- | ```
182+
traverseWithIndex_
183+
:: forall i a b f m
184+
. Applicative m
185+
=> FoldableWithIndex i f
186+
=> (i -> a -> m b)
187+
-> f a
188+
-> m Unit
189+
traverseWithIndex_ f = foldrWithIndex (\i -> (*>) <<< f i) (pure unit)
190+
191+
-- | A version of `traverseWithIndex_` with its arguments flipped.
192+
-- |
193+
-- | This can be useful when running an action written using do notation
194+
-- | for every element in a data structure:
195+
-- |
196+
-- | For example:
197+
-- |
198+
-- | ```purescript
199+
-- | forWithIndex_ ["a", "b", "c"] \i x -> do
200+
-- | logShow i
201+
-- | log x
202+
-- | ```
203+
forWithIndex_
204+
:: forall i a b f m
205+
. Applicative m
206+
=> FoldableWithIndex i f
207+
=> f a
208+
-> (i -> a -> m b)
209+
-> m Unit
210+
forWithIndex_ = flip traverseWithIndex_
211+
212+
-- | `foldMapWithIndex` but with each element surrounded by some fixed value.
213+
-- |
214+
-- | For example:
215+
-- |
216+
-- | ```purescript
217+
-- | > surroundMapWithIndex "*" (\i x -> show i <> x) []
218+
-- | = "*"
219+
-- |
220+
-- | > surroundMapWithIndex "*" (\i x -> show i <> x) ["a"]
221+
-- | = "*0a*"
222+
-- |
223+
-- | > surroundMapWithIndex "*" (\i x -> show i <> x) ["a", "b"]
224+
-- | = "*0a*1b*"
225+
-- |
226+
-- | > surroundMapWithIndex "*" (\i x -> show i <> x) ["a", "b", "c"]
227+
-- | = "*0a*1b*2c*"
228+
-- | ```
229+
surroundMapWithIndex
230+
:: forall i f a m
231+
. FoldableWithIndex i f
232+
=> Semigroup m
233+
=> m
234+
-> (i -> a -> m)
235+
-> f a
236+
-> m
237+
surroundMapWithIndex d t f = unwrap (foldMapWithIndex joined f) d
238+
where joined i a = Endo \m -> d <> t i a <> m
239+
240+
-- | `allWithIndex f` is the same as `and <<< mapWithIndex f`; map a function over the
241+
-- | structure, and then get the conjunction of the results.
242+
allWithIndex
243+
:: forall i a b f
244+
. FoldableWithIndex i f
245+
=> HeytingAlgebra b
246+
=> (i -> a -> b)
247+
-> f a
248+
-> b
249+
allWithIndex t = unwrap <<< foldMapWithIndex (\i -> Conj <<< t i)
250+
251+
-- | `anyWithIndex f` is the same as `or <<< mapWithIndex f`; map a function over the
252+
-- | structure, and then get the disjunction of the results.
253+
anyWithIndex
254+
:: forall i a b f
255+
. FoldableWithIndex i f
256+
=> HeytingAlgebra b
257+
=> (i -> a -> b)
258+
-> f a
259+
-> b
260+
anyWithIndex t = unwrap <<< foldMapWithIndex (\i -> Disj <<< t i)
261+
262+
-- | Try to find an element in a data structure which satisfies a predicate
263+
-- | with access to the index.
264+
findWithIndex
265+
:: forall i a f
266+
. FoldableWithIndex i f
267+
=> (i -> a -> Boolean)
268+
-> f a
269+
-> Maybe a
270+
findWithIndex p = foldlWithIndex go Nothing
271+
where
272+
go i Nothing x | p i x = Just x
273+
go i r _ = r

src/Data/FunctorWithIndex.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"use strict";
2+
3+
exports.mapWithIndexArray = function (f) {
4+
return function (xs) {
5+
var l = xs.length;
6+
var result = Array(l);
7+
for (var i = 0; i < l; i++) {
8+
result[i] = f(i)(xs[i]);
9+
}
10+
return result;
11+
};
12+
};

src/Data/FunctorWithIndex.purs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
module Data.FunctorWithIndex
2+
( class FunctorWithIndex, mapWithIndex
3+
) where
4+
5+
import Prelude
6+
7+
import Data.Maybe (Maybe)
8+
import Data.Maybe.First (First)
9+
import Data.Maybe.Last (Last)
10+
import Data.Monoid.Additive (Additive)
11+
import Data.Monoid.Conj (Conj)
12+
import Data.Monoid.Disj (Disj)
13+
import Data.Monoid.Dual (Dual)
14+
import Data.Monoid.Multiplicative (Multiplicative)
15+
16+
17+
-- | A `Functor` with an additional index.
18+
-- | Instances must satisfy a modified form of the `Functor` laws
19+
-- | ```purescript
20+
-- | mapWithIndex (\_ a -> a) = id
21+
-- | mapWithIndex f . mapWithIndex g = mapWithIndex (\i -> f i <<< g i)
22+
-- | ```
23+
-- | and be compatible with the `Functor` instance
24+
-- | ```purescript
25+
-- | map f = mapWithIndex (const f)
26+
-- | ```
27+
class Functor f <= FunctorWithIndex i f | f -> i where
28+
mapWithIndex :: forall a b. (i -> a -> b) -> f a -> f b
29+
30+
foreign import mapWithIndexArray :: forall i a b. (i -> a -> b) -> Array a -> Array b
31+
32+
instance functorWithIndexArray :: FunctorWithIndex Int Array where
33+
mapWithIndex = mapWithIndexArray
34+
35+
instance functorWithIndexMaybe :: FunctorWithIndex Unit Maybe where
36+
mapWithIndex f = map $ f unit
37+
38+
instance functorWithIndexFirst :: FunctorWithIndex Unit First where
39+
mapWithIndex f = map $ f unit
40+
41+
instance functorWithIndexLast :: FunctorWithIndex Unit Last where
42+
mapWithIndex f = map $ f unit
43+
44+
instance functorWithIndexAdditive :: FunctorWithIndex Unit Additive where
45+
mapWithIndex f = map $ f unit
46+
47+
instance functorWithIndexDual :: FunctorWithIndex Unit Dual where
48+
mapWithIndex f = map $ f unit
49+
50+
instance functorWithIndexConj :: FunctorWithIndex Unit Conj where
51+
mapWithIndex f = map $ f unit
52+
53+
instance functorWithIndexDisj :: FunctorWithIndex Unit Disj where
54+
mapWithIndex f = map $ f unit
55+
56+
instance functorWithIndexMultiplicative :: FunctorWithIndex Unit Multiplicative where
57+
mapWithIndex f = map $ f unit

0 commit comments

Comments
 (0)