Skip to content

Commit c0af6b6

Browse files
committed
Default impls for Foldable & Traversable methods
[Ticket #27](#27) Functions added: * foldMapDefaultL * foldMapDefaultR * foldlDefault * foldrDefault * traverseDefault * sequenceDefault Added tests for each new function. Plus regenerated docs.
1 parent f6a85ae commit c0af6b6

File tree

5 files changed

+201
-17
lines changed

5 files changed

+201
-17
lines changed

docs/Data/Foldable.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,38 @@ instance foldableConj :: Foldable Conj
2828
instance foldableMultiplicative :: Foldable Multiplicative
2929
```
3030

31+
#### `foldrDefault`
32+
33+
``` purescript
34+
foldrDefault :: forall f a b. (Foldable f) => (a -> b -> b) -> b -> f a -> b
35+
```
36+
37+
A default implementation of `foldr` using `foldMap`
38+
39+
#### `foldlDefault`
40+
41+
``` purescript
42+
foldlDefault :: forall f a b. (Foldable f) => (b -> a -> b) -> b -> f a -> b
43+
```
44+
45+
A default implementation of `foldl` using `foldMap`
46+
47+
#### `foldMapDefaultL`
48+
49+
``` purescript
50+
foldMapDefaultL :: forall f a m. (Foldable f, Monoid m) => (a -> m) -> f a -> m
51+
```
52+
53+
A default implementation of `foldMap` using `foldl`
54+
55+
#### `foldMapDefaultR`
56+
57+
``` purescript
58+
foldMapDefaultR :: forall f a m. (Foldable f, Monoid m) => (a -> m) -> f a -> m
59+
```
60+
61+
A default implementation of `foldMap` using `foldr`
62+
3163
#### `fold`
3264

3365
``` purescript

docs/Data/Traversable.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,22 @@ instance traversableDisj :: Traversable Disj
4040
instance traversableMultiplicative :: Traversable Multiplicative
4141
```
4242

43+
#### `traverseDefault`
44+
45+
``` purescript
46+
traverseDefault :: forall t a b m. (Traversable t, Applicative m) => (a -> m b) -> t a -> m (t b)
47+
```
48+
49+
A default implementation of `traverse` using `sequence` and `map`
50+
51+
#### `sequenceDefault`
52+
53+
``` purescript
54+
sequenceDefault :: forall t a m. (Traversable t, Applicative m) => t (m a) -> m (t a)
55+
```
56+
57+
A default implementation of `sequence` using `traverse`
58+
4359
#### `for`
4460

4561
``` purescript

src/Data/Foldable.purs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Data.Foldable
22
( Foldable, foldr, foldl, foldMap
3+
, foldrDefault, foldlDefault, foldMapDefaultL, foldMapDefaultR
34
, fold
45
, traverse_
56
, for_
@@ -25,7 +26,8 @@ import Data.Maybe.First (First(..), runFirst)
2526
import Data.Maybe.Last (Last(..))
2627
import Data.Monoid (Monoid, mempty)
2728
import Data.Monoid.Additive (Additive(..))
28-
import Data.Monoid.Dual (Dual(..))
29+
import Data.Monoid.Dual (Dual(..), runDual)
30+
import Data.Monoid.Endo (Endo(..), runEndo)
2931
import Data.Monoid.Disj (Disj(..), runDisj)
3032
import Data.Monoid.Conj (Conj(..), runConj)
3133
import Data.Monoid.Multiplicative (Multiplicative(..))
@@ -40,10 +42,32 @@ class Foldable f where
4042
foldl :: forall a b. (b -> a -> b) -> b -> f a -> b
4143
foldMap :: forall a m. (Monoid m) => (a -> m) -> f a -> m
4244

45+
46+
-- | A default implementation of `foldr` using `foldMap`
47+
foldrDefault :: forall f a b. (Foldable f) =>
48+
(a -> b -> b) -> b -> f a -> b
49+
foldrDefault c u xs = runEndo (foldMap (Endo <<< c) xs) u
50+
51+
-- | A default implementation of `foldl` using `foldMap`
52+
foldlDefault :: forall f a b. (Foldable f) =>
53+
(b -> a -> b) -> b -> f a -> b
54+
foldlDefault c u xs = runEndo (runDual (foldMap (Dual <<< Endo <<< flip c) xs)) u
55+
56+
-- | A default implementation of `foldMap` using `foldl`
57+
foldMapDefaultL :: forall f a m. (Foldable f, Monoid m) =>
58+
(a -> m) -> f a -> m
59+
foldMapDefaultL f xs = foldl (\acc x -> f x <> acc) mempty xs
60+
61+
-- | A default implementation of `foldMap` using `foldr`
62+
foldMapDefaultR :: forall f a m. (Foldable f, Monoid m) =>
63+
(a -> m) -> f a -> m
64+
foldMapDefaultR f xs = foldr (\x acc -> f x <> acc) mempty xs
65+
66+
4367
instance foldableArray :: Foldable Array where
44-
foldr = foldrArray
45-
foldl = foldlArray
46-
foldMap f xs = foldr (\x acc -> f x <> acc) mempty xs
68+
foldr = foldrArray
69+
foldl = foldlArray
70+
foldMap = foldMapDefaultR
4771

4872
foreign import foldrArray :: forall a b. (a -> b -> b) -> b -> Array a -> b
4973
foreign import foldlArray :: forall a b. (b -> a -> b) -> b -> Array a -> b

src/Data/Traversable.purs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
module Data.Traversable
2-
( Traversable
3-
, traverse
4-
, sequence
2+
( Traversable, traverse, sequence
3+
, traverseDefault, sequenceDefault
54
, for
65
, Accum()
76
, scanl
@@ -44,6 +43,16 @@ class (Functor t, Foldable t) <= Traversable t where
4443
traverse :: forall a b m. (Applicative m) => (a -> m b) -> t a -> m (t b)
4544
sequence :: forall a m. (Applicative m) => t (m a) -> m (t a)
4645

46+
47+
-- | A default implementation of `traverse` using `sequence` and `map`
48+
traverseDefault :: forall t a b m. (Traversable t, Applicative m) => (a -> m b) -> t a -> m (t b)
49+
traverseDefault f ta = sequence (map f ta)
50+
51+
-- | A default implementation of `sequence` using `traverse`
52+
sequenceDefault :: forall t a m. (Traversable t, Applicative m) => t (m a) -> m (t a)
53+
sequenceDefault tma = traverse id tma
54+
55+
4756
foreign import traverseArrayImpl
4857
:: forall m a b. (m (a -> b) -> m a -> m b) ->
4958
((a -> b) -> m a -> m b) ->
@@ -54,7 +63,7 @@ foreign import traverseArrayImpl
5463

5564
instance traversableArray :: Traversable Array where
5665
traverse = traverseArrayImpl apply map pure
57-
sequence = traverse id
66+
sequence = sequenceDefault
5867

5968
instance traversableMaybe :: Traversable Maybe where
6069
traverse _ Nothing = pure Nothing

test/Main.purs

Lines changed: 112 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
21
module Test.Main where
32

43
import Prelude
54

5+
import Control.Monad.Eff (Eff())
66
import Control.Monad.Eff.Console
77
import Data.Foldable
88
import Data.Maybe
@@ -14,30 +14,133 @@ foreign import arrayFrom1UpTo :: Int -> Array Int
1414

1515
main = do
1616
log "Test foldableArray instance"
17-
testFoldableWith 20
17+
testFoldableArrayWith 20
1818

1919
log "Test traversableArray instance"
20-
testTraversableWith 20
20+
testTraversableArrayWith 20
2121

2222
log "Test foldableArray instance is stack safe"
23-
testFoldableWith 20000
23+
testFoldableArrayWith 20000
2424

2525
log "Test traversableArray instance is stack safe"
26-
testTraversableWith 20000
26+
testTraversableArrayWith 20000
27+
28+
log "Test foldMapDefaultL"
29+
testFoldableFoldMapDefaultL 20
30+
31+
log "Test foldMapDefaultR"
32+
testFoldableFoldMapDefaultR 20
33+
34+
log "Test foldlDefault"
35+
testFoldableFoldlDefault 20
36+
37+
log "Test foldrDefault"
38+
testFoldableFoldlDefault 20
39+
40+
log "Test traverseDefault"
41+
testTraverseDefault 20
42+
43+
log "Test sequenceDefault"
44+
testSequenceDefault 20
2745

2846
log "All done!"
2947

30-
testFoldableWith n = do
31-
let arr = arrayFrom1UpTo n
48+
49+
testFoldableFWith :: forall f e. (Foldable f, Eq (f Int)) => (Int -> f Int) -> Int -> Eff (assert :: ASSERT | e) Unit
50+
testFoldableFWith f n = do
51+
let arr = f n
3252
let expectedSum = (n / 2) * (n + 1)
3353

3454
assert $ foldr (+) 0 arr == expectedSum
3555
assert $ foldl (+) 0 arr == expectedSum
3656
assert $ foldMap Additive arr == Additive expectedSum
3757

38-
testTraversableWith n = do
39-
let arr = arrayFrom1UpTo n
58+
testFoldableArrayWith = testFoldableFWith arrayFrom1UpTo
59+
60+
61+
testTraversableFWith :: forall f e. (Traversable f, Eq (f Int)) => (Int -> f Int) -> Int -> Eff (assert :: ASSERT | e) Unit
62+
testTraversableFWith f n = do
63+
let arr = f n
4064

4165
assert $ traverse Just arr == Just arr
4266
assert $ traverse return arr == [arr]
4367
assert $ traverse (\x -> if x < 10 then Just x else Nothing) arr == Nothing
68+
assert $ sequence (map Just arr) == traverse Just arr
69+
70+
testTraversableArrayWith = testTraversableFWith arrayFrom1UpTo
71+
72+
73+
-- structures for testing default `Foldable` implementations
74+
75+
newtype FoldMapDefaultL a = FML (Array a)
76+
newtype FoldMapDefaultR a = FMR (Array a)
77+
newtype FoldlDefault a = FLD (Array a)
78+
newtype FoldrDefault a = FRD (Array a)
79+
80+
instance eqFML :: (Eq a) => Eq (FoldMapDefaultL a) where eq (FML l) (FML r) = l == r
81+
instance eqFMR :: (Eq a) => Eq (FoldMapDefaultR a) where eq (FMR l) (FMR r) = l == r
82+
instance eqFLD :: (Eq a) => Eq (FoldlDefault a) where eq (FLD l) (FLD r) = l == r
83+
instance eqFRD :: (Eq a) => Eq (FoldrDefault a) where eq (FRD l) (FRD r) = l == r
84+
85+
-- implemented `foldl` and `foldr`, but default `foldMap` using `foldl`
86+
instance foldableFML :: Foldable FoldMapDefaultL where
87+
foldMap f = foldMapDefaultL f
88+
foldl f u (FML a) = foldl f u a
89+
foldr f u (FML a) = foldr f u a
90+
91+
-- implemented `foldl` and `foldr`, but default `foldMap`, using `foldr`
92+
instance foldableFMR :: Foldable FoldMapDefaultR where
93+
foldMap f = foldMapDefaultR f
94+
foldl f u (FMR a) = foldl f u a
95+
foldr f u (FMR a) = foldr f u a
96+
97+
-- implemented `foldMap` and `foldr`, but default `foldMap`
98+
instance foldableDFL :: Foldable FoldlDefault where
99+
foldMap f (FLD a) = foldMap f a
100+
foldl f u = foldlDefault f u
101+
foldr f u (FLD a) = foldr f u a
102+
103+
-- implemented `foldMap` and `foldl`, but default `foldr`
104+
instance foldableDFR :: Foldable FoldrDefault where
105+
foldMap f (FRD a) = foldMap f a
106+
foldl f u (FRD a) = foldl f u a
107+
foldr f u = foldrDefault f u
108+
109+
testFoldableFoldMapDefaultL = testFoldableFWith (FML <<< arrayFrom1UpTo)
110+
testFoldableFoldMapDefaultR = testFoldableFWith (FMR <<< arrayFrom1UpTo)
111+
testFoldableFoldlDefault = testFoldableFWith (FLD <<< arrayFrom1UpTo)
112+
testFoldableFoldrDefault = testFoldableFWith (FRD <<< arrayFrom1UpTo)
113+
114+
115+
-- structures for testing default `Traversable` implementations
116+
117+
newtype TraverseDefault a = TD (Array a)
118+
newtype SequenceDefault a = SD (Array a)
119+
120+
instance eqTD :: (Eq a) => Eq (TraverseDefault a) where eq (TD l) (TD r) = l == r
121+
instance eqSD :: (Eq a) => Eq (SequenceDefault a) where eq (SD l) (SD r) = l == r
122+
123+
instance functorTD :: Functor TraverseDefault where map f (TD a) = TD (map f a)
124+
instance functorSD :: Functor SequenceDefault where map f (SD a) = SD (map f a)
125+
126+
instance foldableTD :: Foldable TraverseDefault where
127+
foldMap f (TD a) = foldMap f a
128+
foldr f u (TD a) = foldr f u a
129+
foldl f u (TD a) = foldl f u a
130+
131+
instance foldableSD :: Foldable SequenceDefault where
132+
foldMap f (SD a) = foldMap f a
133+
foldr f u (SD a) = foldr f u a
134+
foldl f u (SD a) = foldl f u a
135+
136+
instance traversableTD :: Traversable TraverseDefault where
137+
traverse f = traverseDefault f
138+
sequence (TD a) = map TD (sequence a)
139+
140+
instance traversableSD :: Traversable SequenceDefault where
141+
traverse f (SD a) = map SD (traverse f a)
142+
sequence m = sequenceDefault m
143+
144+
testTraverseDefault = testTraversableFWith (TD <<< arrayFrom1UpTo)
145+
testSequenceDefault = testTraversableFWith (SD <<< arrayFrom1UpTo)
146+

0 commit comments

Comments
 (0)