Skip to content

Commit 91297ed

Browse files
authored
Merge pull request #22 from purescript/unfoldable1
Add `Unfoldable1`
2 parents 21bd871 + 8c4c6f3 commit 91297ed

File tree

3 files changed

+92
-6
lines changed

3 files changed

+92
-6
lines changed

src/Data/Unfoldable.purs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ import Data.Tuple (Tuple(..), fst, snd)
2222

2323
import Partial.Unsafe (unsafePartial)
2424

25-
-- | This class identifies data structures which can be _unfolded_,
26-
-- | generalizing `unfoldr` on arrays.
25+
-- | This class identifies data structures which can be _unfolded_.
2726
-- |
2827
-- | The generating function `f` in `unfoldr f` in understood as follows:
2928
-- |

src/Data/Unfoldable1.purs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
module Data.Unfoldable1
2+
( class Unfoldable1, unfoldr1
3+
, replicate1
4+
, replicate1A
5+
, singleton
6+
, range
7+
) where
8+
9+
import Prelude
10+
11+
import Data.Maybe (Maybe(..))
12+
import Data.Semigroup.Traversable (class Traversable1, sequence1)
13+
import Data.Tuple (Tuple(..))
14+
15+
-- | This class identifies non-empty data structures which can be _unfolded_.
16+
-- |
17+
-- | The generating function `f` corresponds to the `uncons` operation of a
18+
-- | non-empty list or array; it always return a value, and then optionally
19+
-- | a value to continue unfolding from.
20+
class Unfoldable1 t where
21+
unfoldr1 :: forall a b. (b -> Tuple a (Maybe b)) -> b -> t a
22+
23+
-- | Replicate a value `n` times. At least one value will be produced, so values
24+
-- | `n < 1` less than one will be ignored.
25+
-- |
26+
-- | ``` purescript
27+
-- | replicate1 0 "foo" == NEL.singleton "foo" :: NEL.NonEmptyList String
28+
-- | replicate1 2 "foo" == NEL.cons "foo" (NEL.singleton "foo") :: NEL.NonEmptyList String
29+
-- | ```
30+
replicate1 :: forall f a. Unfoldable1 f => Int -> a -> f a
31+
replicate1 n v = unfoldr1 step (n - 1)
32+
where
33+
step :: Int -> Tuple a (Maybe Int)
34+
step i
35+
| i <= 0 = Tuple v Nothing
36+
| otherwise = Tuple v (Just (i - 1))
37+
38+
-- | Perform an `Apply` action `n` times (at least once, so values `n < 1`
39+
-- | less than one will be ignored), and accumulate the results.
40+
replicate1A
41+
:: forall m f a
42+
. Apply m
43+
=> Unfoldable1 f
44+
=> Traversable1 f
45+
=> Int
46+
-> m a
47+
-> m (f a)
48+
replicate1A n m = sequence1 (replicate1 n m)
49+
50+
-- | Contain a single value. For example:
51+
-- |
52+
-- | ``` purescript
53+
-- | singleton "foo" == NEL.singleton "foo" :: NEL.NonEmptyList String
54+
-- | ```
55+
singleton :: forall f a. Unfoldable1 f => a -> f a
56+
singleton = replicate1 1
57+
58+
-- | Create an `Unfoldable1` containing a range of values, including both
59+
-- | endpoints.
60+
-- |
61+
-- | ``` purescript
62+
-- | range 0 0 "foo" == NEL.singleton 0 :: NEL.NonEmptyList Int
63+
-- | range 1 2 "foo" == NEL.cons 1 (NEL.singleton 2) :: NEL.NonEmptyList Int
64+
-- | range 2 0 "foo" == NEL.cons 2 (NEL.cons 1 (NEL.singleton 0)) :: NEL.NonEmptyList Int
65+
-- | ```
66+
range :: forall f. Unfoldable1 f => Int -> Int -> f Int
67+
range start end =
68+
let delta = if end >= start then 1 else -1 in unfoldr1 (go delta) start
69+
where
70+
go delta i =
71+
let i' = i + delta
72+
in Tuple i (if i == end then Nothing else Just i')

test/Main.purs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@ import Prelude
44

55
import Control.Monad.Eff (Eff)
66
import Control.Monad.Eff.Console (CONSOLE, log, logShow)
7-
87
import Data.Maybe (Maybe(..))
9-
import Data.Tuple (Tuple(..))
8+
import Data.Tuple (Tuple(..), uncurry)
109
import Data.Unfoldable as U
11-
10+
import Data.Unfoldable1 as U1
1211
import Test.Assert (ASSERT, assert)
1312

13+
data NonEmpty f a = NonEmpty a (f a)
14+
15+
derive instance eqNonEmpty :: (Eq (f a), Eq a) => Eq (NonEmpty f a)
16+
17+
instance unfoldable1NonEmpty :: U.Unfoldable f => U1.Unfoldable1 (NonEmpty f) where
18+
unfoldr1 f = uncurry NonEmpty <<< map (U.unfoldr $ map f) <<< f
19+
1420
collatz :: Int -> Array Int
1521
collatz = U.unfoldr step
1622
where
@@ -32,21 +38,30 @@ main = do
3238

3339
log "Test singleton"
3440
assert $ U.singleton unit == [unit]
41+
assert $ U1.singleton unit == NonEmpty unit []
3542

3643
log "Test replicate"
44+
assert $ U.replicate 0 "foo" == []
3745
assert $ U.replicate 3 "foo" == ["foo", "foo", "foo"]
46+
assert $ U1.replicate1 0 "foo" == NonEmpty "foo" []
47+
assert $ U1.replicate1 3 "foo" == NonEmpty "foo" ["foo", "foo"]
3848

3949
log "Test replicateA"
4050
assert $ U.replicateA 3 [1,2] == [
4151
[1,1,1],[1,1,2], [1,2,1],[1,2,2],
4252
[2,1,1],[2,1,2], [2,2,1],[2,2,2]
4353
]
4454

45-
log "Test range"
55+
log "Test U.range"
4656
assert $ U.range 1 0 == []
4757
assert $ U.range 0 0 == [0]
4858
assert $ U.range 0 2 == [0, 1, 2]
4959

60+
log "Test U1.range"
61+
assert $ U1.range 1 0 == NonEmpty 1 [0]
62+
assert $ U1.range 0 0 == NonEmpty 0 []
63+
assert $ U1.range 0 2 == NonEmpty 0 [1, 2]
64+
5065
log "Test Maybe.toUnfoldable"
5166
assert $ U.fromMaybe (Just "a") == ["a"]
5267
assert $ U.fromMaybe (Nothing :: Maybe String) == []

0 commit comments

Comments
 (0)