├── .eslintrc.json ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── package.json ├── src └── Data │ ├── Unfoldable.js │ ├── Unfoldable.purs │ ├── Unfoldable1.js │ └── Unfoldable1.purs └── test └── Main.purs /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module" 5 | }, 6 | "extends": "eslint:recommended", 7 | "rules": { 8 | "strict": [2, "global"], 9 | "block-scoped-var": 2, 10 | "consistent-return": 2, 11 | "eqeqeq": [2, "smart"], 12 | "guard-for-in": 2, 13 | "no-caller": 2, 14 | "no-extend-native": 2, 15 | "no-loop-func": 2, 16 | "no-new": 2, 17 | "no-param-reassign": 2, 18 | "no-return-assign": 2, 19 | "no-unused-expressions": 2, 20 | "no-use-before-define": 2, 21 | "radix": [2, "always"], 22 | "indent": [2, 2], 23 | "quotes": [2, "double"], 24 | "semi": [2, "always"] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Description of the change** 2 | 3 | Clearly and concisely describe the purpose of the pull request. If this PR relates to an existing issue or change proposal, please link to it. Include any other background context that would help reviewers understand the motivation for this PR. 4 | 5 | --- 6 | 7 | **Checklist:** 8 | 9 | - [ ] Added the change to the changelog's "Unreleased" section with a reference to this PR (e.g. "- Made a change (#0000)") 10 | - [ ] Linked any existing issues or proposals that this pull request should close 11 | - [ ] Updated or added relevant documentation 12 | - [ ] Added a test for the contribution (if applicable) 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - uses: purescript-contrib/setup-purescript@main 16 | with: 17 | purescript: "unstable" 18 | 19 | - uses: actions/setup-node@v2 20 | with: 21 | node-version: "14.x" 22 | 23 | - name: Install dependencies 24 | run: | 25 | npm install -g bower 26 | npm install 27 | bower install --production 28 | 29 | - name: Build source 30 | run: npm run-script build 31 | 32 | - name: Run tests 33 | run: | 34 | bower install 35 | npm run-script test --if-present 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.* 2 | !/.gitignore 3 | !/.eslintrc.json 4 | !/.github/ 5 | /bower_components/ 6 | /node_modules/ 7 | /output/ 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 4 | 5 | ## [Unreleased] 6 | 7 | Breaking changes: 8 | 9 | New features: 10 | 11 | Bugfixes: 12 | 13 | Other improvements: 14 | 15 | ## [v6.0.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v6.0.0) - 2022-04-27 16 | 17 | Breaking changes: 18 | - Migrate FFI to ES modules (#37 by @kl0tl and @JordanMartinez) 19 | 20 | New features: 21 | - Add `iterateN` function (#20 by @matthewleon and @JordanMartinez) 22 | 23 | Bugfixes: 24 | 25 | Other improvements: 26 | 27 | ## [v5.0.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v5.0.0) - 2021-02-26 28 | 29 | Breaking changes: 30 | - Added support for PureScript 0.14 and dropped support for all previous versions (#29) 31 | 32 | New features: 33 | 34 | Bugfixes: 35 | 36 | Other improvements: 37 | - Migrated CI to GitHub Actions and updated installation instructions to use Spago (#30) 38 | - Added a changelog and pull request template (#32) 39 | 40 | ## [v4.1.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v4.1.0) - 2020-01-26 41 | 42 | - Added instances for `Maybe` 43 | 44 | ## [v4.0.2](https://github.com/purescript/purescript-unfoldable/releases/tag/v4.0.2) - 2019-05-29 45 | 46 | - Documentation improvements: give every function at least one example, and fix a couple of minor errors. 47 | 48 | ## [v4.0.1](https://github.com/purescript/purescript-unfoldable/releases/tag/v4.0.1) - 2019-05-28 49 | 50 | - Improved documentation to clarify the relationship between `Unfoldable` and `Unfoldable1`. 51 | 52 | ## [v4.0.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v4.0.0) - 2018-05-23 53 | 54 | - Updated for PureScript 0.12 55 | - `Unfoldable1` is now a superclass of `Unfoldable` (if a value can be unfolded from something that is possibly empty, it can certainly be unfolded from something that is not empty). This also matches the relationship of `Foldable` and `Foldable1`, albeit in reverse. 56 | 57 | ## [v3.2.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v3.2.0) - 2018-04-07 58 | 59 | - Added `Unfoldable1` class (@matthewleon, @garyb) 60 | 61 | ## [v3.1.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v3.1.0) - 2017-12-10 62 | 63 | - Added `range` for unfolding a range of integers (@matthewleon) 64 | 65 | ## [v3.0.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v3.0.0) - 2017-03-26 66 | 67 | - Updated for PureScript 0.11 68 | 69 | ## [v2.0.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v2.0.0) - 2016-10-07 70 | 71 | - Updated dependencies 72 | 73 | ## [v1.1.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v1.1.0) - 2016-09-30 74 | 75 | - Added `fromMaybe` (@eskimor) 76 | 77 | ## [v1.0.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v1.0.0) - 2016-06-01 78 | 79 | This release is intended for the PureScript 0.9.1 compiler and newer. 80 | 81 | **Note**: The v1.0.0 tag is not meant to indicate the library is “finished”, the core libraries are all being bumped to this for the 0.9 compiler release so as to use semver more correctly. 82 | 83 | - Removed dependency on `purescript-arrays` so as to enable a `toUnfoldable` implementation in `Data.Array` 84 | 85 | ## [v0.4.1](https://github.com/purescript/purescript-unfoldable/releases/tag/v0.4.1) - 2015-09-26 86 | 87 | - Added `replicate`, `replicateA`, `none`, and `singleton` (@LiamGoodacre) 88 | 89 | ## [v0.4.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v0.4.0) - 2015-06-30 90 | 91 | This release works with versions 0.7.\* of the PureScript compiler. It will not work with older versions. If you are using an older version, you should require an older, compatible version of this library. 92 | 93 | ## [v0.3.2](https://github.com/purescript/purescript-unfoldable/releases/tag/v0.3.2) - 2015-03-18 94 | 95 | - Updated docs 96 | 97 | ## [v0.3.1](https://github.com/purescript/purescript-unfoldable/releases/tag/v0.3.1) - 2015-03-17 98 | 99 | - Updated docs 100 | 101 | ## [v0.3.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v0.3.0) - 2015-02-21 102 | 103 | **This release requires PureScript v0.6.8 or later** 104 | - Updated dependencies 105 | 106 | ## [v0.2.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v0.2.0) - 2014-11-28 107 | 108 | - Fix arrays dependency 109 | 110 | ## [v0.1.0](https://github.com/purescript/purescript-unfoldable/releases/tag/v0.1.0) - 2014-08-27 111 | 112 | - Initial release 113 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 PureScript 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without 15 | specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 21 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 24 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # purescript-unfoldable 2 | 3 | [![Latest release](http://img.shields.io/github/release/purescript/purescript-unfoldable.svg)](https://github.com/purescript/purescript-unfoldable/releases) 4 | [![Build status](https://github.com/purescript/purescript-unfoldable/workflows/CI/badge.svg?branch=master)](https://github.com/purescript/purescript-unfoldable/actions?query=workflow%3ACI+branch%3Amaster) 5 | [![Pursuit](https://pursuit.purescript.org/packages/purescript-unfoldable/badge)](https://pursuit.purescript.org/packages/purescript-unfoldable) 6 | 7 | Unfoldable functors. 8 | 9 | ## Installation 10 | 11 | ``` 12 | spago install unfoldable 13 | ``` 14 | 15 | ## Documentation 16 | 17 | Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-unfoldable). 18 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "purescript-unfoldable", 3 | "homepage": "https://github.com/purescript/purescript-unfoldable", 4 | "license": "BSD-3-Clause", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/purescript/purescript-unfoldable.git" 8 | }, 9 | "ignore": [ 10 | "**/.*", 11 | "bower_components", 12 | "node_modules", 13 | "output", 14 | "test", 15 | "bower.json", 16 | "package.json" 17 | ], 18 | "dependencies": { 19 | "purescript-foldable-traversable": "^6.0.0", 20 | "purescript-maybe": "^6.0.0", 21 | "purescript-partial": "^4.0.0", 22 | "purescript-prelude": "^6.0.0", 23 | "purescript-tuples": "^7.0.0" 24 | }, 25 | "devDependencies": { 26 | "purescript-assert": "^6.0.0", 27 | "purescript-console": "^6.0.0", 28 | "purescript-psci-support": "^6.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "clean": "rimraf output && rimraf .pulp-cache", 5 | "build": "eslint src && pulp build -- --censor-lib --strict", 6 | "test": "pulp test" 7 | }, 8 | "devDependencies": { 9 | "eslint": "^7.15.0", 10 | "pulp": "16.0.0-0", 11 | "purescript-psa": "^0.8.2", 12 | "rimraf": "^3.0.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Data/Unfoldable.js: -------------------------------------------------------------------------------- 1 | export const unfoldrArrayImpl = function (isNothing) { 2 | return function (fromJust) { 3 | return function (fst) { 4 | return function (snd) { 5 | return function (f) { 6 | return function (b) { 7 | var result = []; 8 | var value = b; 9 | while (true) { // eslint-disable-line no-constant-condition 10 | var maybe = f(value); 11 | if (isNothing(maybe)) return result; 12 | var tuple = fromJust(maybe); 13 | result.push(fst(tuple)); 14 | value = snd(tuple); 15 | } 16 | }; 17 | }; 18 | }; 19 | }; 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /src/Data/Unfoldable.purs: -------------------------------------------------------------------------------- 1 | -- | This module provides a type class for _unfoldable functors_, i.e. 2 | -- | functors which support an `unfoldr` operation. 3 | -- | 4 | -- | This allows us to unify various operations on arrays, lists, 5 | -- | sequences, etc. 6 | 7 | module Data.Unfoldable 8 | ( class Unfoldable, unfoldr 9 | , replicate 10 | , replicateA 11 | , none 12 | , fromMaybe 13 | , module Data.Unfoldable1 14 | ) where 15 | 16 | import Prelude 17 | 18 | import Data.Maybe (Maybe(..), isNothing, fromJust) 19 | import Data.Traversable (class Traversable, sequence) 20 | import Data.Tuple (Tuple(..), fst, snd) 21 | import Data.Unfoldable1 (class Unfoldable1, unfoldr1, singleton, range, iterateN, replicate1, replicate1A) 22 | import Partial.Unsafe (unsafePartial) 23 | 24 | -- | This class identifies (possibly empty) data structures which can be 25 | -- | _unfolded_. 26 | -- | 27 | -- | The generating function `f` in `unfoldr f` is understood as follows: 28 | -- | 29 | -- | - If `f b` is `Nothing`, then `unfoldr f b` should be empty. 30 | -- | - If `f b` is `Just (Tuple a b1)`, then `unfoldr f b` should consist of `a` 31 | -- | appended to the result of `unfoldr f b1`. 32 | -- | 33 | -- | Note that it is not possible to give `Unfoldable` instances to types which 34 | -- | represent structures which are guaranteed to be non-empty, such as 35 | -- | `NonEmptyArray`: consider what `unfoldr (const Nothing)` should produce. 36 | -- | Structures which are guaranteed to be non-empty can instead be given 37 | -- | `Unfoldable1` instances. 38 | class Unfoldable1 t <= Unfoldable t where 39 | unfoldr :: forall a b. (b -> Maybe (Tuple a b)) -> b -> t a 40 | 41 | instance unfoldableArray :: Unfoldable Array where 42 | unfoldr = unfoldrArrayImpl isNothing (unsafePartial fromJust) fst snd 43 | 44 | instance unfoldableMaybe :: Unfoldable Maybe where 45 | unfoldr f b = fst <$> f b 46 | 47 | foreign import unfoldrArrayImpl 48 | :: forall a b 49 | . (forall x. Maybe x -> Boolean) 50 | -> (forall x. Maybe x -> x) 51 | -> (forall x y. Tuple x y -> x) 52 | -> (forall x y. Tuple x y -> y) 53 | -> (b -> Maybe (Tuple a b)) 54 | -> b 55 | -> Array a 56 | 57 | -- | Replicate a value some natural number of times. 58 | -- | For example: 59 | -- | 60 | -- | ``` purescript 61 | -- | replicate 2 "foo" == (["foo", "foo"] :: Array String) 62 | -- | ``` 63 | replicate :: forall f a. Unfoldable f => Int -> a -> f a 64 | replicate n v = unfoldr step n 65 | where 66 | step :: Int -> Maybe (Tuple a Int) 67 | step i = 68 | if i <= 0 then Nothing 69 | else Just (Tuple v (i - 1)) 70 | 71 | -- | Perform an Applicative action `n` times, and accumulate all the results. 72 | -- | 73 | -- | ``` purescript 74 | -- | > replicateA 5 (randomInt 1 10) :: Effect (Array Int) 75 | -- | [1,3,2,7,5] 76 | -- | ``` 77 | replicateA 78 | :: forall m f a 79 | . Applicative m 80 | => Unfoldable f 81 | => Traversable f 82 | => Int 83 | -> m a 84 | -> m (f a) 85 | replicateA n m = sequence (replicate n m) 86 | 87 | -- | The container with no elements - unfolded with zero iterations. 88 | -- | For example: 89 | -- | 90 | -- | ``` purescript 91 | -- | none == ([] :: Array Unit) 92 | -- | ``` 93 | none :: forall f a. Unfoldable f => f a 94 | none = unfoldr (const Nothing) unit 95 | 96 | -- | Convert a Maybe to any Unfoldable, such as lists or arrays. 97 | -- | 98 | -- | ``` purescript 99 | -- | fromMaybe (Nothing :: Maybe Int) == [] 100 | -- | fromMaybe (Just 1) == [1] 101 | -- | ``` 102 | fromMaybe :: forall f a. Unfoldable f => Maybe a -> f a 103 | fromMaybe = unfoldr (\b -> flip Tuple Nothing <$> b) 104 | -------------------------------------------------------------------------------- /src/Data/Unfoldable1.js: -------------------------------------------------------------------------------- 1 | export const unfoldr1ArrayImpl = function (isNothing) { 2 | return function (fromJust) { 3 | return function (fst) { 4 | return function (snd) { 5 | return function (f) { 6 | return function (b) { 7 | var result = []; 8 | var value = b; 9 | while (true) { // eslint-disable-line no-constant-condition 10 | var tuple = f(value); 11 | result.push(fst(tuple)); 12 | var maybe = snd(tuple); 13 | if (isNothing(maybe)) return result; 14 | value = fromJust(maybe); 15 | } 16 | }; 17 | }; 18 | }; 19 | }; 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /src/Data/Unfoldable1.purs: -------------------------------------------------------------------------------- 1 | module Data.Unfoldable1 2 | ( class Unfoldable1, unfoldr1 3 | , replicate1 4 | , replicate1A 5 | , singleton 6 | , range 7 | , iterateN 8 | ) where 9 | 10 | import Prelude 11 | 12 | import Data.Maybe (Maybe(..), fromJust, isNothing) 13 | import Data.Semigroup.Traversable (class Traversable1, sequence1) 14 | import Data.Tuple (Tuple(..), fst, snd) 15 | import Partial.Unsafe (unsafePartial) 16 | 17 | -- | This class identifies data structures which can be _unfolded_. 18 | -- | 19 | -- | The generating function `f` in `unfoldr1 f` corresponds to the `uncons` 20 | -- | operation of a non-empty list or array; it always returns a value, and 21 | -- | then optionally a value to continue unfolding from. 22 | -- | 23 | -- | Note that, in order to provide an `Unfoldable1 t` instance, `t` need not 24 | -- | be a type which is guaranteed to be non-empty. For example, the fact that 25 | -- | lists can be empty does not prevent us from providing an 26 | -- | `Unfoldable1 List` instance. However, the result of `unfoldr1` should 27 | -- | always be non-empty. 28 | -- | 29 | -- | Every type which has an `Unfoldable` instance can be given an 30 | -- | `Unfoldable1` instance (and, in fact, is required to, because 31 | -- | `Unfoldable1` is a superclass of `Unfoldable`). However, there are types 32 | -- | which have `Unfoldable1` instances but cannot have `Unfoldable` instances. 33 | -- | In particular, types which are guaranteed to be non-empty, such as 34 | -- | `NonEmptyList`, cannot be given `Unfoldable` instances. 35 | -- | 36 | -- | The utility of this class, then, is that it provides an `Unfoldable`-like 37 | -- | interface while still permitting instances for guaranteed-non-empty types 38 | -- | like `NonEmptyList`. 39 | class Unfoldable1 t where 40 | unfoldr1 :: forall a b. (b -> Tuple a (Maybe b)) -> b -> t a 41 | 42 | instance unfoldable1Array :: Unfoldable1 Array where 43 | unfoldr1 = unfoldr1ArrayImpl isNothing (unsafePartial fromJust) fst snd 44 | 45 | instance unfoldable1Maybe :: Unfoldable1 Maybe where 46 | unfoldr1 f b = Just (fst (f b)) 47 | 48 | foreign import unfoldr1ArrayImpl 49 | :: forall a b 50 | . (forall x. Maybe x -> Boolean) 51 | -> (forall x. Maybe x -> x) 52 | -> (forall x y. Tuple x y -> x) 53 | -> (forall x y. Tuple x y -> y) 54 | -> (b -> Tuple a (Maybe b)) 55 | -> b 56 | -> Array a 57 | 58 | -- | Replicate a value `n` times. At least one value will be produced, so values 59 | -- | `n` less than 1 will be treated as 1. 60 | -- | 61 | -- | ``` purescript 62 | -- | replicate1 2 "foo" == (NEL.cons "foo" (NEL.singleton "foo") :: NEL.NonEmptyList String) 63 | -- | replicate1 0 "foo" == (NEL.singleton "foo" :: NEL.NonEmptyList String) 64 | -- | ``` 65 | replicate1 :: forall f a. Unfoldable1 f => Int -> a -> f a 66 | replicate1 n v = unfoldr1 step (n - 1) 67 | where 68 | step :: Int -> Tuple a (Maybe Int) 69 | step i 70 | | i <= 0 = Tuple v Nothing 71 | | otherwise = Tuple v (Just (i - 1)) 72 | 73 | -- | Perform an `Apply` action `n` times (at least once, so values `n` less 74 | -- | than 1 will be treated as 1), and accumulate the results. 75 | -- | 76 | -- | ``` purescript 77 | -- | > replicate1A 2 (randomInt 1 10) :: Effect (NEL.NonEmptyList Int) 78 | -- | (NonEmptyList (NonEmpty 8 (2 : Nil))) 79 | -- | > replicate1A 0 (randomInt 1 10) :: Effect (NEL.NonEmptyList Int) 80 | -- | (NonEmptyList (NonEmpty 4 Nil)) 81 | -- | ``` 82 | replicate1A 83 | :: forall m f a 84 | . Apply m 85 | => Unfoldable1 f 86 | => Traversable1 f 87 | => Int 88 | -> m a 89 | -> m (f a) 90 | replicate1A n m = sequence1 (replicate1 n m) 91 | 92 | -- | Contain a single value. For example: 93 | -- | 94 | -- | ``` purescript 95 | -- | singleton "foo" == (NEL.singleton "foo" :: NEL.NonEmptyList String) 96 | -- | ``` 97 | singleton :: forall f a. Unfoldable1 f => a -> f a 98 | singleton = replicate1 1 99 | 100 | -- | Create an `Unfoldable1` containing a range of values, including both 101 | -- | endpoints. 102 | -- | 103 | -- | ``` purescript 104 | -- | range 0 0 == (NEL.singleton 0 :: NEL.NonEmptyList Int) 105 | -- | range 1 2 == (NEL.cons 1 (NEL.singleton 2) :: NEL.NonEmptyList Int) 106 | -- | range 2 0 == (NEL.cons 2 (NEL.cons 1 (NEL.singleton 0)) :: NEL.NonEmptyList Int) 107 | -- | ``` 108 | range :: forall f. Unfoldable1 f => Int -> Int -> f Int 109 | range start end = 110 | let delta = if end >= start then 1 else -1 in unfoldr1 (go delta) start 111 | where 112 | go delta i = 113 | let i' = i + delta 114 | in Tuple i (if i == end then Nothing else Just i') 115 | 116 | -- | Create an `Unfoldable1` by repeated application of a function to a seed value. 117 | -- | For example: 118 | -- | 119 | -- | ``` purescript 120 | -- | (iterateN 5 (_ + 1) 0 :: Array Int) == [0, 1, 2, 3, 4] 121 | -- | (iterateN 5 (_ + 1) 0 :: NonEmptyArray Int) == NonEmptyArray [0, 1, 2, 3, 4] 122 | -- | 123 | -- | (iterateN 0 (_ + 1) 0 :: Array Int) == [0] 124 | -- | (iterateN 0 (_ + 1) 0 :: NonEmptyArray Int) == NonEmptyArray [0] 125 | -- | ``` 126 | iterateN :: forall f a. Unfoldable1 f => Int -> (a -> a) -> a -> f a 127 | iterateN n f s = unfoldr1 go $ Tuple s (n - 1) 128 | where 129 | go (Tuple x n') = Tuple x 130 | if n' > 0 then Just $ Tuple (f x) $ n' - 1 131 | else Nothing 132 | -------------------------------------------------------------------------------- /test/Main.purs: -------------------------------------------------------------------------------- 1 | module Test.Main where 2 | 3 | import Prelude 4 | 5 | import Data.Eq (class Eq1) 6 | import Data.Maybe (Maybe(..)) 7 | import Data.Tuple (Tuple(..), uncurry) 8 | import Data.Unfoldable as U 9 | import Data.Unfoldable1 as U1 10 | import Effect (Effect) 11 | import Effect.Console (log, logShow) 12 | import Test.Assert (assert) 13 | 14 | data NonEmpty f a = NonEmpty a (f a) 15 | 16 | derive instance eqNonEmpty :: (Eq1 f, Eq a) => Eq (NonEmpty f a) 17 | 18 | instance unfoldable1NonEmpty :: U.Unfoldable f => U1.Unfoldable1 (NonEmpty f) where 19 | unfoldr1 f = uncurry NonEmpty <<< map (U.unfoldr $ map f) <<< f 20 | 21 | collatz :: Int -> Array Int 22 | collatz = U.unfoldr step 23 | where 24 | step 1 = Nothing 25 | step n = 26 | Just $ 27 | Tuple n $ 28 | if n `mod` 2 == 0 29 | then n / 2 30 | else n * 3 + 1 31 | 32 | main :: Effect Unit 33 | main = do 34 | log "Collatz 1000" 35 | logShow $ collatz 1000 36 | 37 | log "Test none" 38 | assert $ U.none == ([] :: Array Unit) 39 | 40 | log "Test singleton" 41 | assert $ U.singleton unit == [unit] 42 | assert $ U1.singleton unit == NonEmpty unit [] 43 | 44 | log "Test replicate" 45 | assert $ U.replicate 0 "foo" == [] 46 | assert $ U.replicate 3 "foo" == ["foo", "foo", "foo"] 47 | assert $ U1.replicate1 0 "foo" == NonEmpty "foo" [] 48 | assert $ U1.replicate1 3 "foo" == NonEmpty "foo" ["foo", "foo"] 49 | 50 | log "Test replicateA" 51 | assert $ U.replicateA 3 [1,2] == [ 52 | [1,1,1],[1,1,2], [1,2,1],[1,2,2], 53 | [2,1,1],[2,1,2], [2,2,1],[2,2,2] 54 | ] 55 | 56 | log "Test range" 57 | assert $ U1.range 1 0 == [1, 0] 58 | assert $ U1.range 0 0 == [0] 59 | assert $ U1.range 0 2 == [0, 1, 2] 60 | assert $ U1.range 1 0 == NonEmpty 1 [0] 61 | assert $ U1.range 0 0 == NonEmpty 0 [] 62 | assert $ U1.range 0 2 == NonEmpty 0 [1, 2] 63 | 64 | log "Test iterateN" 65 | assert $ U.iterateN 5 (_ + 1) 0 == [0, 1, 2, 3, 4] 66 | 67 | log "Test Maybe.toUnfoldable" 68 | assert $ U.fromMaybe (Just "a") == ["a"] 69 | assert $ U.fromMaybe (Nothing :: Maybe String) == [] 70 | 71 | log "All done!" 72 | --------------------------------------------------------------------------------