Skip to content

Commit

Permalink
Merge pull request #22 from purescript/unfoldable1
Browse files Browse the repository at this point in the history
Add `Unfoldable1`
  • Loading branch information
garyb authored Apr 7, 2018
2 parents 21bd871 + 8c4c6f3 commit 91297ed
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 6 deletions.
3 changes: 1 addition & 2 deletions src/Data/Unfoldable.purs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ import Data.Tuple (Tuple(..), fst, snd)

import Partial.Unsafe (unsafePartial)

-- | This class identifies data structures which can be _unfolded_,
-- | generalizing `unfoldr` on arrays.
-- | This class identifies data structures which can be _unfolded_.
-- |
-- | The generating function `f` in `unfoldr f` in understood as follows:
-- |
Expand Down
72 changes: 72 additions & 0 deletions src/Data/Unfoldable1.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
module Data.Unfoldable1
( class Unfoldable1, unfoldr1
, replicate1
, replicate1A
, singleton
, range
) where

import Prelude

import Data.Maybe (Maybe(..))
import Data.Semigroup.Traversable (class Traversable1, sequence1)
import Data.Tuple (Tuple(..))

-- | This class identifies non-empty data structures which can be _unfolded_.
-- |
-- | The generating function `f` corresponds to the `uncons` operation of a
-- | non-empty list or array; it always return a value, and then optionally
-- | a value to continue unfolding from.
class Unfoldable1 t where
unfoldr1 :: forall a b. (b -> Tuple a (Maybe b)) -> b -> t a

-- | Replicate a value `n` times. At least one value will be produced, so values
-- | `n < 1` less than one will be ignored.
-- |
-- | ``` purescript
-- | replicate1 0 "foo" == NEL.singleton "foo" :: NEL.NonEmptyList String
-- | replicate1 2 "foo" == NEL.cons "foo" (NEL.singleton "foo") :: NEL.NonEmptyList String
-- | ```
replicate1 :: forall f a. Unfoldable1 f => Int -> a -> f a
replicate1 n v = unfoldr1 step (n - 1)
where
step :: Int -> Tuple a (Maybe Int)
step i
| i <= 0 = Tuple v Nothing
| otherwise = Tuple v (Just (i - 1))

-- | Perform an `Apply` action `n` times (at least once, so values `n < 1`
-- | less than one will be ignored), and accumulate the results.
replicate1A
:: forall m f a
. Apply m
=> Unfoldable1 f
=> Traversable1 f
=> Int
-> m a
-> m (f a)
replicate1A n m = sequence1 (replicate1 n m)

-- | Contain a single value. For example:
-- |
-- | ``` purescript
-- | singleton "foo" == NEL.singleton "foo" :: NEL.NonEmptyList String
-- | ```
singleton :: forall f a. Unfoldable1 f => a -> f a
singleton = replicate1 1

-- | Create an `Unfoldable1` containing a range of values, including both
-- | endpoints.
-- |
-- | ``` purescript
-- | range 0 0 "foo" == NEL.singleton 0 :: NEL.NonEmptyList Int
-- | range 1 2 "foo" == NEL.cons 1 (NEL.singleton 2) :: NEL.NonEmptyList Int
-- | range 2 0 "foo" == NEL.cons 2 (NEL.cons 1 (NEL.singleton 0)) :: NEL.NonEmptyList Int
-- | ```
range :: forall f. Unfoldable1 f => Int -> Int -> f Int
range start end =
let delta = if end >= start then 1 else -1 in unfoldr1 (go delta) start
where
go delta i =
let i' = i + delta
in Tuple i (if i == end then Nothing else Just i')
23 changes: 19 additions & 4 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ import Prelude

import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log, logShow)

import Data.Maybe (Maybe(..))
import Data.Tuple (Tuple(..))
import Data.Tuple (Tuple(..), uncurry)
import Data.Unfoldable as U

import Data.Unfoldable1 as U1
import Test.Assert (ASSERT, assert)

data NonEmpty f a = NonEmpty a (f a)

derive instance eqNonEmpty :: (Eq (f a), Eq a) => Eq (NonEmpty f a)

instance unfoldable1NonEmpty :: U.Unfoldable f => U1.Unfoldable1 (NonEmpty f) where
unfoldr1 f = uncurry NonEmpty <<< map (U.unfoldr $ map f) <<< f

collatz :: Int -> Array Int
collatz = U.unfoldr step
where
Expand All @@ -32,21 +38,30 @@ main = do

log "Test singleton"
assert $ U.singleton unit == [unit]
assert $ U1.singleton unit == NonEmpty unit []

log "Test replicate"
assert $ U.replicate 0 "foo" == []
assert $ U.replicate 3 "foo" == ["foo", "foo", "foo"]
assert $ U1.replicate1 0 "foo" == NonEmpty "foo" []
assert $ U1.replicate1 3 "foo" == NonEmpty "foo" ["foo", "foo"]

log "Test replicateA"
assert $ U.replicateA 3 [1,2] == [
[1,1,1],[1,1,2], [1,2,1],[1,2,2],
[2,1,1],[2,1,2], [2,2,1],[2,2,2]
]

log "Test range"
log "Test U.range"
assert $ U.range 1 0 == []
assert $ U.range 0 0 == [0]
assert $ U.range 0 2 == [0, 1, 2]

log "Test U1.range"
assert $ U1.range 1 0 == NonEmpty 1 [0]
assert $ U1.range 0 0 == NonEmpty 0 []
assert $ U1.range 0 2 == NonEmpty 0 [1, 2]

log "Test Maybe.toUnfoldable"
assert $ U.fromMaybe (Just "a") == ["a"]
assert $ U.fromMaybe (Nothing :: Maybe String) == []
Expand Down

0 comments on commit 91297ed

Please sign in to comment.