diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76a7124..d2aa6cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,30 +5,27 @@ on: branches: [main] pull_request: +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - uses: purescript-contrib/setup-purescript@main - with: - purescript: "unstable" + - uses: actions/checkout@v6 - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v6 with: - node-version: "14.x" + node-version: "24" - name: Install dependencies - run: | - npm install -g bower - npm install - bower install --production + run: npm install - name: Build source - run: npm run-script build + run: npm run build - name: Run tests - run: | - bower install - npm run-script test --if-present + run: npm test + + - name: Check formatting + run: npm run format:check diff --git a/.gitignore b/.gitignore index 70df87e..6b169de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,13 @@ .* !.gitignore !.github +!/.tidyrc.json +!/.oxfmtrc.json output generated-docs bower_components node_modules -package-lock.json -*.lock +/package-lock.json +/spago.lock diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..757fd64 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,3 @@ +{ + "trailingComma": "es5" +} diff --git a/.tidyrc.json b/.tidyrc.json new file mode 100644 index 0000000..9e39cf1 --- /dev/null +++ b/.tidyrc.json @@ -0,0 +1,10 @@ +{ + "importSort": "ide", + "importWrap": "source", + "indent": 2, + "operatorsFile": null, + "ribbon": 1, + "typeArrowPlacement": "first", + "unicode": "never", + "width": null +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 62126f0..d8cfd2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ 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). -## [Unreleased] +## [Unreleased](https://github.com/purescript/purescript-filterable/compare/v5.0.1...HEAD) Breaking changes: @@ -12,9 +12,16 @@ Bugfixes: Other improvements: +## [v5.0.1](https://github.com/purescript/purescript-filterable/releases/tag/v5.0.1) - 2026-10-17 + +Other improvements: + +- Migrate from Bower/Pulp to Spago and update CI, tooling, and formatting (#25 by @thomashoneyman) + ## [v5.0.0](https://github.com/purescript/purescript-filterable/releases/tag/v5.0.0) - 2022-04-27 Breaking changes: + - Update project and deps to PureScript v0.15.0 (#23 by @JordanMartinez) New features: @@ -26,6 +33,7 @@ Other improvements: ## [v4.0.1](https://github.com/purescript/purescript-filterable/releases/tag/v4.0.1) - 2021-04-19 Other improvements: + - Renamed default branch to `main` from `master` (@thomashoneyman) - Fix UnusedName warnings revealed by v0.14.1 PureScript release (#22 by @JordanMartinez) diff --git a/bower.json b/bower.json deleted file mode 100644 index 83f75ca..0000000 --- a/bower.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "purescript-filterable", - "homepage": "https://github.com/purescript/purescript-filterable", - "description": "Classes for filterable and witherable data structures", - "author": "Liam Goodacre", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/purescript/purescript-filterable.git" - }, - "ignore": [ - "**/.*", - "bower_components", - "node_modules", - "output", - "test", - "bower.json", - "package.json" - ], - "dependencies": { - "purescript-foldable-traversable": "^6.0.0", - "purescript-identity": "^6.0.0", - "purescript-arrays": "^7.0.0", - "purescript-either": "^6.0.0", - "purescript-lists": "^7.0.0", - "purescript-ordered-collections": "^3.0.0" - }, - "devDependencies": { - "purescript-assert": "^6.0.0", - "purescript-console": "^6.0.0" - } -} diff --git a/package.json b/package.json index a7db390..4d6dfef 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,16 @@ { "private": true, "scripts": { - "clean": "rimraf output && rimraf .pulp-cache", - "build": "pulp build -- --censor-lib --strict", - "test": "pulp test", - "build:docs": "rimraf docs && pulp docs" + "build": "spago build", + "test": "spago test", + "build:docs": "spago docs", + "format": "purs-tidy format-in-place src test && oxfmt --write .", + "format:check": "purs-tidy check src test && oxfmt --check ." }, "devDependencies": { - "pulp": "16.0.0-0", - "purescript-psa": "^0.8.2", - "rimraf": "^3.0.2" + "oxfmt": "^0.73.0", + "purescript": "^0.15.16", + "purs-tidy": "^0.11.1", + "spago": "^1.0.5" } } diff --git a/spago.yaml b/spago.yaml new file mode 100644 index 0000000..1efb75a --- /dev/null +++ b/spago.yaml @@ -0,0 +1,34 @@ +package: + name: filterable + description: Classes for filterable and witherable data structures + build: + strict: true + pedanticPackages: true + publish: + version: 5.0.1 + license: MIT + location: + githubOwner: purescript + githubRepo: purescript-filterable + dependencies: + - arrays: ">=7.0.0 <8.0.0" + - control: ">=6.0.0 <7.0.0" + - either: ">=6.0.0 <7.0.0" + - foldable-traversable: ">=6.0.0 <7.0.0" + - identity: ">=6.0.0 <7.0.0" + - lists: ">=7.0.0 <8.0.0" + - maybe: ">=6.0.0 <7.0.0" + - newtype: ">=5.0.0 <6.0.0" + - ordered-collections: ">=3.0.0 <4.0.0" + - prelude: ">=6.0.0 <7.0.0" + - st: ">=6.0.0 <7.0.0" + - tuples: ">=7.0.0 <8.0.0" + test: + main: Test.Main + strict: true + pedanticPackages: true + dependencies: + - assert: ">=6.0.0 <7.0.0" + - console: ">=6.0.0 <7.0.0" + - effect: ">=4.0.0 <5.0.0" +workspace: {} diff --git a/src/Data/Compactable.purs b/src/Data/Compactable.purs index 2e6be9d..7641d54 100644 --- a/src/Data/Compactable.purs +++ b/src/Data/Compactable.purs @@ -64,53 +64,69 @@ import Prelude (class Ord, const, discard, unit, void, (<<<)) -- | - `compact (const Nothing <$> xs) ≡ empty` class Compactable f where - compact :: forall a. - f (Maybe a) -> f a - - separate :: forall l r. - f (Either l r) -> { left :: f l, right :: f r } - -compactDefault :: forall f a. Functor f => Compactable f => - f (Maybe a) -> f a + compact + :: forall a + . f (Maybe a) + -> f a + + separate + :: forall l r + . f (Either l r) + -> { left :: f l, right :: f r } + +compactDefault + :: forall f a + . Functor f + => Compactable f + => f (Maybe a) + -> f a compactDefault = _.right <<< separate <<< map (note unit) -separateDefault :: forall f l r. Functor f => Compactable f => - f (Either l r) -> { left :: f l, right :: f r} -separateDefault xs = { left: compact $ (hush <<< swapEither) <$> xs - , right: compact $ hush <$> xs - } +separateDefault + :: forall f l r + . Functor f + => Compactable f + => f (Either l r) + -> { left :: f l, right :: f r } +separateDefault xs = + { left: compact $ (hush <<< swapEither) <$> xs + , right: compact $ hush <$> xs + } where - swapEither e = case e of - Left x -> Right x - Right y -> Left y + swapEither e = case e of + Left x -> Right x + Right y -> Left y instance compactableMaybe :: Compactable Maybe where compact = join - separate Nothing = { left: Nothing, right: Nothing } - separate (Just e) = case e of - Left l -> { left: Just l, right: Nothing } - Right r -> { left: Nothing, right: Just r } + separate = case _ of + Nothing -> { left: Nothing, right: Nothing } + Just e -> case e of + Left l -> { left: Just l, right: Nothing } + Right r -> { left: Nothing, right: Just r } instance compactableEither :: Monoid m => Compactable (Either m) where - compact (Left m) = Left m - compact (Right m) = case m of - Just v -> Right v - Nothing -> Left mempty - - separate (Left x) = { left: Left x, right: Left x } - separate (Right e) = case e of - Left l -> { left: Right l, right: Left mempty } - Right r -> { left: Left mempty, right: Right r } + compact = case _ of + Left m -> Left m + Right m -> case m of + Just v -> Right v + Nothing -> Left mempty + + separate = case _ of + Left x -> { left: Left x, right: Left x } + Right e -> case e of + Left l -> { left: Right l, right: Left mempty } + Right r -> { left: Left mempty, right: Right r } instance compactableArray :: Compactable Array where compact xs = ST.run do result <- STA.new - iter <- STAI.iterator (xs !! _) + iter <- STAI.iterator (xs !! _) STAI.iterate iter $ void <<< case _ of Nothing -> pure 0 - Just j -> STA.push j result + Just j -> STA.push j result STA.unsafeFreeze result @@ -120,45 +136,69 @@ instance compactableArray :: Compactable Array where iter <- STAI.iterator (xs !! _) STAI.iterate iter $ void <<< case _ of - Left l -> STA.push l ls + Left l -> STA.push l ls Right r -> STA.push r rs - {left: _, right: _} <$> STA.unsafeFreeze ls <*> STA.unsafeFreeze rs + { left: _, right: _ } <$> STA.unsafeFreeze ls <*> STA.unsafeFreeze rs instance compactableList :: Compactable List.List where compact = List.catMaybes - separate = foldl go { left: empty, right: empty } where + separate = foldl go { left: empty, right: empty } + where go acc = case _ of - Left l -> acc { left = acc.left <|> pure l } + Left l -> acc { left = acc.left <|> pure l } Right r -> acc { right = acc.right <|> pure r } instance compactableMap :: Ord k => Compactable (Map.Map k) where compact = foldr select Map.empty <<< mapToList where - select (Tuple k x) m = Map.alter (const x) k m + select (Tuple k x) m = Map.alter (const x) k m separate = foldr select { left: Map.empty, right: Map.empty } <<< mapToList where - select (Tuple k v) { left, right } = case v of - Left l -> { left: Map.insert k l left, right } - Right r -> { left: left, right: Map.insert k r right } - -mapToList :: forall k v. Ord k => - Map.Map k v -> List.List (Tuple k v) + select (Tuple k v) { left, right } = case v of + Left l -> { left: Map.insert k l left, right } + Right r -> { left: left, right: Map.insert k r right } + +mapToList + :: forall k v + . Ord k + => Map.Map k v + -> List.List (Tuple k v) mapToList = Map.toUnfoldable -applyMaybe :: forall f a b. Apply f => Compactable f => - f (a -> Maybe b) -> f a -> f b +applyMaybe + :: forall f a b + . Apply f + => Compactable f + => f (a -> Maybe b) + -> f a + -> f b applyMaybe p = compact <<< apply p -applyEither :: forall f a l r. Apply f => Compactable f => - f (a -> Either l r) -> f a -> { left :: f l, right :: f r } +applyEither + :: forall f a l r + . Apply f + => Compactable f + => f (a -> Either l r) + -> f a + -> { left :: f l, right :: f r } applyEither p = separate <<< apply p -bindMaybe :: forall m a b. Bind m => Compactable m => - m a -> (a -> m (Maybe b)) -> m b +bindMaybe + :: forall m a b + . Bind m + => Compactable m + => m a + -> (a -> m (Maybe b)) + -> m b bindMaybe x = compact <<< bind x -bindEither :: forall m a l r. Bind m => Compactable m => - m a -> (a -> m (Either l r)) -> { left :: m l, right :: m r } +bindEither + :: forall m a l r + . Bind m + => Compactable m + => m a + -> (a -> m (Either l r)) + -> { left :: m l, right :: m r } bindEither x = separate <<< bind x diff --git a/src/Data/Filterable.purs b/src/Data/Filterable.purs index 860d213..3e91bc4 100644 --- a/src/Data/Filterable.purs +++ b/src/Data/Filterable.purs @@ -20,19 +20,19 @@ module Data.Filterable import Control.Bind ((=<<)) import Control.Category ((<<<)) -import Data.Array (partition, mapMaybe, filter) as Array +import Data.Array (filter, mapMaybe, partition) as Array import Data.Compactable (class Compactable, compact, separate) import Data.Either (Either(..)) import Data.Foldable (foldl, foldr) import Data.Functor (class Functor, map) import Data.HeytingAlgebra (not) import Data.List (List(..), filter, mapMaybe) as List -import Data.Map (Map, empty, insert, alter, toUnfoldable) as Map +import Data.Map (Map, alter, empty, insert, toUnfoldable) as Map import Data.Maybe (Maybe(..)) import Data.Monoid (class Monoid, mempty) import Data.Semigroup ((<>)) import Data.Tuple (Tuple(..)) -import Prelude (const, class Ord) +import Prelude (class Ord, const) -- | `Filterable` represents data structures which can be _partitioned_/_filtered_. -- | @@ -67,88 +67,142 @@ import Prelude (const, class Ord) -- | - `filterDefaultPartitionMap` -- | - `filterMapDefault` class (Compactable f, Functor f) <= Filterable f where - partitionMap :: forall a l r. - (a -> Either l r) -> f a -> { left :: f l, right :: f r } - - partition :: forall a. - (a -> Boolean) -> f a -> { no :: f a, yes :: f a } - - filterMap :: forall a b. - (a -> Maybe b) -> f a -> f b - - filter :: forall a. - (a -> Boolean) -> f a -> f a + partitionMap + :: forall a l r + . (a -> Either l r) + -> f a + -> { left :: f l, right :: f r } + + partition + :: forall a + . (a -> Boolean) + -> f a + -> { no :: f a, yes :: f a } + + filterMap + :: forall a b + . (a -> Maybe b) + -> f a + -> f b + + filter + :: forall a + . (a -> Boolean) + -> f a + -> f a -- | Upgrade a boolean-style predicate to an either-style predicate mapping. -eitherBool :: forall a. - (a -> Boolean) -> a -> Either a a +eitherBool + :: forall a + . (a -> Boolean) + -> a + -> Either a a eitherBool p x = if p x then Right x else Left x -- | Upgrade a boolean-style predicate to a maybe-style predicate mapping. -maybeBool :: forall a. - (a -> Boolean) -> a -> Maybe a +maybeBool + :: forall a + . (a -> Boolean) + -> a + -> Maybe a maybeBool p x = if p x then Just x else Nothing -- | A default implementation of `partitionMap` using `separate`. Note that this is -- | almost certainly going to be suboptimal compared to direct implementations. -partitionMapDefault :: forall f a l r. Filterable f => - (a -> Either l r) -> f a -> { left :: f l, right :: f r } +partitionMapDefault + :: forall f a l r + . Filterable f + => (a -> Either l r) + -> f a + -> { left :: f l, right :: f r } partitionMapDefault p = separate <<< map p -- | A default implementation of `partition` using `partitionMap`. -partitionDefault :: forall f a. Filterable f => - (a -> Boolean) -> f a -> { no :: f a, yes :: f a } -partitionDefault p xs = +partitionDefault + :: forall f a + . Filterable f + => (a -> Boolean) + -> f a + -> { no :: f a, yes :: f a } +partitionDefault p xs = do let o = partitionMap (eitherBool p) xs - in {no: o.left, yes: o.right} + { no: o.left, yes: o.right } -- | A default implementation of `partition` using `filter`. Note that this is -- | almost certainly going to be suboptimal compared to direct implementations. -partitionDefaultFilter :: forall f a. Filterable f => - (a -> Boolean) -> f a -> { no :: f a, yes :: f a } +partitionDefaultFilter + :: forall f a + . Filterable f + => (a -> Boolean) + -> f a + -> { no :: f a, yes :: f a } partitionDefaultFilter p xs = { yes: filter p xs, no: filter (not p) xs } -- | A default implementation of `filterMap` using `separate`. Note that this is -- | almost certainly going to be suboptimal compared to direct implementations. -filterMapDefault :: forall f a b. Filterable f => - (a -> Maybe b) -> f a -> f b +filterMapDefault + :: forall f a b + . Filterable f + => (a -> Maybe b) + -> f a + -> f b filterMapDefault p = compact <<< map p -- | A default implementation of `partition` using `filterMap`. Note that this -- | is almost certainly going to be suboptimal compared to direct -- | implementations. -partitionDefaultFilterMap :: forall f a. Filterable f => - (a -> Boolean) -> f a -> { no :: f a, yes :: f a } +partitionDefaultFilterMap + :: forall f a + . Filterable f + => (a -> Boolean) + -> f a + -> { no :: f a, yes :: f a } partitionDefaultFilterMap p xs = { yes: filterMap (maybeBool p) xs , no: filterMap (maybeBool (not p)) xs } -- | A default implementation of `filter` using `filterMap`. -filterDefault :: forall f a. Filterable f => - (a -> Boolean) -> f a -> f a +filterDefault + :: forall f a + . Filterable f + => (a -> Boolean) + -> f a + -> f a filterDefault = filterMap <<< maybeBool -- | A default implementation of `filter` using `partition`. -filterDefaultPartition :: forall f a. Filterable f => - (a -> Boolean) -> f a -> f a +filterDefaultPartition + :: forall f a + . Filterable f + => (a -> Boolean) + -> f a + -> f a filterDefaultPartition p xs = (partition p xs).yes -- | A default implementation of `filter` using `partitionMap`. -filterDefaultPartitionMap :: forall f a. Filterable f => - (a -> Boolean) -> f a -> f a +filterDefaultPartitionMap + :: forall f a + . Filterable f + => (a -> Boolean) + -> f a + -> f a filterDefaultPartitionMap p xs = (partitionMap (eitherBool p) xs).right -- | Filter out all values. -cleared :: forall f a b. Filterable f => - f a -> f b +cleared + :: forall f a b + . Filterable f + => f a + -> f b cleared = filterMap (const Nothing) instance filterableArray :: Filterable Array where - partitionMap p = foldl go {left: [], right: []} where + partitionMap p = foldl go { left: [], right: [] } + where go acc x = case p x of - Left l -> acc { left = acc.left <> [l] } - Right r -> acc { right = acc.right <> [r] } + Left l -> acc { left = acc.left <> [ l ] } + Right r -> acc { right = acc.right <> [ r ] } partition = Array.partition @@ -157,10 +211,11 @@ instance filterableArray :: Filterable Array where filter = Array.filter instance filterableMaybe :: Filterable Maybe where - partitionMap _ Nothing = { left: Nothing, right: Nothing } - partitionMap p (Just x) = case p x of - Left a -> { left: Just a, right: Nothing } - Right b -> { left: Nothing, right: Just b } + partitionMap p = case _ of + Nothing -> { left: Nothing, right: Nothing } + Just x -> case p x of + Left a -> { left: Just a, right: Nothing } + Right b -> { left: Nothing, right: Just b } partition p = partitionDefault p @@ -169,17 +224,19 @@ instance filterableMaybe :: Filterable Maybe where filter p = filterDefault p instance filterableEither :: Monoid m => Filterable (Either m) where - partitionMap _ (Left x) = { left: Left x, right: Left x } - partitionMap p (Right x) = case p x of - Left a -> { left: Right a, right: Left mempty } - Right b -> { left: Left mempty, right: Right b } + partitionMap p = case _ of + Left x -> { left: Left x, right: Left x } + Right x -> case p x of + Left a -> { left: Right a, right: Left mempty } + Right b -> { left: Left mempty, right: Right b } partition p = partitionDefault p - filterMap _ (Left l) = Left l - filterMap p (Right r) = case p r of - Nothing -> Left mempty - Just x -> Right x + filterMap p = case _ of + Left l -> Left l + Right r -> case p r of + Nothing -> Left mempty + Just x -> Right x filter p = filterDefault p @@ -187,17 +244,19 @@ instance filterableList :: Filterable List.List where -- partitionMap :: forall a l r. (a -> Either l r) -> List a -> { left :: List l, right :: List r } partitionMap p xs = foldr select { left: List.Nil, right: List.Nil } xs where - select x { left, right } = case p x of - Left l -> { left: List.Cons l left, right } - Right r -> { left, right: List.Cons r right } + select x { left, right } = case p x of + Left l -> { left: List.Cons l left, right } + Right r -> { left, right: List.Cons r right } -- partition :: forall a. (a -> Boolean) -> List a -> { no :: List a, yes :: List a } partition p xs = foldr select { no: List.Nil, yes: List.Nil } xs where - -- select :: (a -> Boolean) -> a -> { no :: List a, yes :: List a } -> { no :: List a, yes :: List a } - select x { no, yes } = if p x - then { no, yes: List.Cons x yes } - else { no: List.Cons x no, yes } + -- select :: (a -> Boolean) -> a -> { no :: List a, yes :: List a } -> { no :: List a, yes :: List a } + select x { no, yes } = + if p x then + { no, yes: List.Cons x yes } + else + { no: List.Cons x no, yes } -- filterMap :: forall a b. (a -> Maybe b) -> List a -> List b filterMap p = List.mapMaybe p @@ -209,21 +268,21 @@ instance filterableMap :: Ord k => Filterable (Map.Map k) where partitionMap p xs = foldr select { left: Map.empty, right: Map.empty } (toList xs) where - toList :: forall v. Map.Map k v -> List.List (Tuple k v) - toList = Map.toUnfoldable + toList :: forall v. Map.Map k v -> List.List (Tuple k v) + toList = Map.toUnfoldable - select (Tuple k x) { left, right } = case p x of - Left l -> { left: Map.insert k l left, right } - Right r -> { left, right: Map.insert k r right } + select (Tuple k x) { left, right } = case p x of + Left l -> { left: Map.insert k l left, right } + Right r -> { left, right: Map.insert k r right } partition p = partitionDefault p filterMap p xs = foldr select Map.empty (toList xs) where - toList :: forall v. Map.Map k v -> List.List (Tuple k v) - toList = Map.toUnfoldable + toList :: forall v. Map.Map k v -> List.List (Tuple k v) + toList = Map.toUnfoldable - select (Tuple k x) m = Map.alter (const (p x)) k m + select (Tuple k x) m = Map.alter (const (p x)) k m filter p = filterDefault p diff --git a/src/Data/Witherable.purs b/src/Data/Witherable.purs index 1dfa232..8efb010 100644 --- a/src/Data/Witherable.purs +++ b/src/Data/Witherable.purs @@ -12,8 +12,8 @@ module Data.Witherable , module Data.Filterable ) where -import Control.Applicative (class Applicative, (<*>), pure) -import Control.Category ((<<<), identity) +import Control.Applicative (class Applicative, pure, (<*>)) +import Control.Category (identity, (<<<)) import Data.Compactable (compact, separate) import Data.Either (Either(..)) import Data.Filterable (class Filterable) @@ -57,45 +57,84 @@ import Prelude (class Ord) -- | - `filterMapByWither` -- | - `traverseByWither` class (Filterable t, Traversable t) <= Witherable t where - wilt :: forall m a l r. Applicative m => - (a -> m (Either l r)) -> t a -> m { left :: t l, right :: t r } - - wither :: forall m a b. Applicative m => - (a -> m (Maybe b)) -> t a -> m (t b) + wilt + :: forall m a l r + . Applicative m + => (a -> m (Either l r)) + -> t a + -> m { left :: t l, right :: t r } + + wither + :: forall m a b + . Applicative m + => (a -> m (Maybe b)) + -> t a + -> m (t b) -- | A default implementation of `wilt` using `separate` -wiltDefault :: forall t m a l r. Witherable t => Applicative m => - (a -> m (Either l r)) -> t a -> m { left :: t l, right :: t r } +wiltDefault + :: forall t m a l r + . Witherable t + => Applicative m + => (a -> m (Either l r)) + -> t a + -> m { left :: t l, right :: t r } wiltDefault p = map separate <<< traverse p -- | A default implementation of `wither` using `compact`. -witherDefault :: forall t m a b. Witherable t => Applicative m => - (a -> m (Maybe b)) -> t a -> m (t b) +witherDefault + :: forall t m a b + . Witherable t + => Applicative m + => (a -> m (Maybe b)) + -> t a + -> m (t b) witherDefault p = map compact <<< traverse p -- | A default implementation of `partitionMap` given a `Witherable`. -partitionMapByWilt :: forall t a l r. Witherable t => - (a -> Either l r) -> t a -> { left :: t l, right :: t r } +partitionMapByWilt + :: forall t a l r + . Witherable t + => (a -> Either l r) + -> t a + -> { left :: t l, right :: t r } partitionMapByWilt p = unwrap <<< wilt (Identity <<< p) -- | A default implementation of `filterMap` given a `Witherable`. -filterMapByWither :: forall t a b. Witherable t => - (a -> Maybe b) -> t a -> t b +filterMapByWither + :: forall t a b + . Witherable t + => (a -> Maybe b) + -> t a + -> t b filterMapByWither p = unwrap <<< wither (Identity <<< p) -- | A default implementation of `traverse` given a `Witherable`. -traverseByWither :: forall t m a b. Witherable t => Applicative m => - (a -> m b) -> t a -> m (t b) +traverseByWither + :: forall t m a b + . Witherable t + => Applicative m + => (a -> m b) + -> t a + -> m (t b) traverseByWither f = wither (map Just <<< f) -- | Partition between `Left` and `Right` values - with effects in `m`. -wilted :: forall t m l r. Witherable t => Applicative m => - t (m (Either l r)) -> m { left :: t l, right :: t r } +wilted + :: forall t m l r + . Witherable t + => Applicative m + => t (m (Either l r)) + -> m { left :: t l, right :: t r } wilted = wilt identity -- | Filter out all the `Nothing` values - with effects in `m`. -withered :: forall t m x. Witherable t => Applicative m => - t (m (Maybe x)) -> m (t x) +withered + :: forall t m x + . Witherable t + => Applicative m + => t (m (Maybe x)) + -> m (t x) withered = wither identity instance witherableArray :: Witherable Array where @@ -103,60 +142,72 @@ instance witherableArray :: Witherable Array where wither = witherDefault instance witherableList :: Witherable List where - wilt p = map rev <<< List.foldl go (pure { left: Nil, right: Nil }) where + wilt p = map rev <<< List.foldl go (pure { left: Nil, right: Nil }) + where rev { left, right } = { left: List.reverse left, right: List.reverse right } - go acc x = (\{left, right} -> - case _ of - Left l -> { left: l : left, right } + go acc x = add <$> acc <*> p x + where + add { left, right } = case _ of + Left l -> { left: l : left, right } Right r -> { left, right: r : right } - ) <$> acc <*> p x - wither p = map List.reverse <<< List.foldl go (pure Nil) where - go acc x = (\comp -> - case _ of + wither p = map List.reverse <<< List.foldl go (pure Nil) + where + go acc x = add <$> acc <*> p x + where + add comp = case _ of Nothing -> comp - Just j -> j : comp - ) <$> acc <*> p x + Just j -> j : comp instance witherableMap :: Ord k => Witherable (Map.Map k) where wilt p = List.foldl go (pure { left: Map.empty, right: Map.empty }) <<< toList where - toList :: forall v. Ord k => Map.Map k v -> List.List (Tuple k v) - toList = Map.toUnfoldable + toList :: forall v. Ord k => Map.Map k v -> List.List (Tuple k v) + toList = Map.toUnfoldable - go acc (Tuple k x) = (\{left, right} -> - case _ of - Left l -> { left: Map.insert k l left, right } - Right r -> { left, right: Map.insert k r right } - ) <$> acc <*> p x + go acc (Tuple k x) = add <$> acc <*> p x + where + add { left, right } = case _ of + Left l -> { left: Map.insert k l left, right } + Right r -> { left, right: Map.insert k r right } wither p = List.foldl go (pure Map.empty) <<< toList where - toList :: forall v. Ord k => Map.Map k v -> List.List (Tuple k v) - toList = Map.toUnfoldable + toList :: forall v. Ord k => Map.Map k v -> List.List (Tuple k v) + toList = Map.toUnfoldable - go acc (Tuple k x) = (\comp -> - case _ of - Nothing -> comp - Just j -> Map.insert k j comp - ) <$> acc <*> p x + go acc (Tuple k x) = add <$> acc <*> p x + where + add comp = case _ of + Nothing -> comp + Just j -> Map.insert k j comp instance witherableMaybe :: Witherable Maybe where - wilt _ Nothing = pure { left: Nothing, right: Nothing } - wilt p (Just x) = map convert (p x) where - convert (Left l) = { left: Just l, right: Nothing } - convert (Right r) = { left: Nothing, right: Just r } + wilt p = case _ of + Nothing -> pure { left: Nothing, right: Nothing } + Just x -> map convert (p x) + where + convert = case _ of + Left l -> { left: Just l, right: Nothing } + Right r -> { left: Nothing, right: Just r } - wither _ Nothing = pure Nothing - wither p (Just x) = p x + wither p = case _ of + Nothing -> pure Nothing + Just x -> p x instance witherableEither :: Monoid m => Witherable (Either m) where - wilt _ (Left el) = pure { left: Left el, right: Left el } - wilt p (Right er) = map convert (p er) where - convert (Left l) = { left: Right l, right: Left mempty } - convert (Right r) = { left: Left mempty, right: Right r } - - wither _ (Left el) = pure (Left el) - wither p (Right er) = map convert (p er) where - convert Nothing = Left mempty - convert (Just r) = Right r + wilt p = case _ of + Left el -> pure { left: Left el, right: Left el } + Right er -> map convert (p er) + where + convert = case _ of + Left l -> { left: Right l, right: Left mempty } + Right r -> { left: Left mempty, right: Right r } + + wither p = case _ of + Left el -> pure (Left el) + Right er -> map convert (p er) + where + convert = case _ of + Nothing -> Left mempty + Just r -> Right r diff --git a/test/Main.purs b/test/Main.purs index 08bf482..984d8a3 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -19,270 +19,334 @@ testCompactable :: Effect Unit testCompactable = do log "Test compactableMaybe instance" *> do let parts1 = separate $ Just ((Left 1) :: Either Int Int) - assertEqual { actual: parts1.left - , expected: Just 1 - } - assertEqual { actual: parts1.right - , expected: Nothing - } + assertEqual + { actual: parts1.left + , expected: Just 1 + } + assertEqual + { actual: parts1.right + , expected: Nothing + } let parts2 = separate $ Just ((Right 2) :: Either Int Int) - assertEqual { actual: parts2.left - , expected: Nothing - } - assertEqual { actual: parts2.right - , expected: Just 2 - } + assertEqual + { actual: parts2.left + , expected: Nothing + } + assertEqual + { actual: parts2.right + , expected: Just 2 + } let parts3 = separate $ (Nothing :: Maybe (Either Int Int)) - assertEqual { actual: parts3.left - , expected: Nothing - } - assertEqual { actual: parts3.right - , expected: Nothing - } + assertEqual + { actual: parts3.left + , expected: Nothing + } + assertEqual + { actual: parts3.right + , expected: Nothing + } log "Text compactableEither instance" *> do - assertEqual { actual: compact (Left [1] :: Either (Array Int) (Maybe Int)) - , expected: Left [1] - } + assertEqual + { actual: compact (Left [ 1 ] :: Either (Array Int) (Maybe Int)) + , expected: Left [ 1 ] + } - assertEqual { actual: compact (Right Nothing :: Either (Array Int) (Maybe Int)) - , expected: Left [] - } + assertEqual + { actual: compact (Right Nothing :: Either (Array Int) (Maybe Int)) + , expected: Left [] + } - assertEqual { actual: compact (Right (Just 3) :: Either (Array Int) (Maybe Int)) - , expected: Right 3 - } + assertEqual + { actual: compact (Right (Just 3) :: Either (Array Int) (Maybe Int)) + , expected: Right 3 + } - let parts1 = separate (Left [1] :: Either (Array Int) (Either Int Int)) - assertEqual { actual: parts1.left - , expected: Left [1] - } - assertEqual { actual: parts1.right - , expected: Left [1] - } + let parts1 = separate (Left [ 1 ] :: Either (Array Int) (Either Int Int)) + assertEqual + { actual: parts1.left + , expected: Left [ 1 ] + } + assertEqual + { actual: parts1.right + , expected: Left [ 1 ] + } let parts2 = separate (Right (Left 2) :: Either (Array Int) (Either Int Int)) - assertEqual { actual: parts2.left - , expected: Right 2 - } - assertEqual { actual: parts2.right - , expected: Left [] - } + assertEqual + { actual: parts2.left + , expected: Right 2 + } + assertEqual + { actual: parts2.right + , expected: Left [] + } let parts3 = separate (Right (Right 3) :: Either (Array Int) (Either Int Int)) - assertEqual { actual: parts3.left - , expected: Left [] - } - assertEqual { actual: parts3.right - , expected: Right 3 - } + assertEqual + { actual: parts3.left + , expected: Left [] + } + assertEqual + { actual: parts3.right + , expected: Right 3 + } log "Test compactableArray instance" *> do - let testList = [Left 1, Right 2, Left 3, Right 4, Left 5, Right 6, Left 7, Right 8] + let testList = [ Left 1, Right 2, Left 3, Right 4, Left 5, Right 6, Left 7, Right 8 ] let parts = separate testList - assertEqual { actual: parts.left - , expected: [1, 3, 5, 7] - } - assertEqual { actual: parts.right - , expected: [2, 4, 6, 8] - } + assertEqual + { actual: parts.left + , expected: [ 1, 3, 5, 7 ] + } + assertEqual + { actual: parts.right + , expected: [ 2, 4, 6, 8 ] + } log "Test compactableList instance" *> do let testList = (Left 1 : Right 2 : Left 3 : Right 4 : Left 5 : Right 6 : Left 7 : Right 8 : Nil) let parts = separate testList - assertEqual { actual: parts.left - , expected: 1 : 3 : 5 : 7 : Nil - } - assertEqual { actual: parts.right - , expected: 2 : 4 : 6 : 8 : Nil - } + assertEqual + { actual: parts.left + , expected: 1 : 3 : 5 : 7 : Nil + } + assertEqual + { actual: parts.right + , expected: 2 : 4 : 6 : 8 : Nil + } log "Test compactableMap instance" *> do let m = Map.fromFoldable - assertEqual { actual: compact $ m [1 /\ Just 1, 2 /\ Nothing, 3 /\ Just 3, 4 /\ Nothing] - , expected: m [1 /\ 1, 3 /\ 3] - } + assertEqual + { actual: compact $ m [ 1 /\ Just 1, 2 /\ Nothing, 3 /\ Just 3, 4 /\ Nothing ] + , expected: m [ 1 /\ 1, 3 /\ 3 ] + } - let parts = separate $ m [1 /\ Left 1, 2 /\ Right 2, 3 /\ Left 3, 4 /\ Right 4] - assertEqual { actual: parts.left - , expected: m [1 /\ 1, 3 /\ 3] - } - assertEqual { actual: parts.right - , expected: m [2 /\ 2, 4 /\ 4] - } + let parts = separate $ m [ 1 /\ Left 1, 2 /\ Right 2, 3 /\ Left 3, 4 /\ Right 4 ] + assertEqual + { actual: parts.left + , expected: m [ 1 /\ 1, 3 /\ 3 ] + } + assertEqual + { actual: parts.right + , expected: m [ 2 /\ 2, 4 /\ 4 ] + } testFilterable :: Effect Unit testFilterable = do log "Test filterableMaybe instance" *> do - assertEqual { actual: filterMap pred (Just 6) - , expected: Just 60 - } - assertEqual { actual: filterMap pred (Just 5) - , expected: Nothing - } - assertEqual { actual: filterMap pred Nothing - , expected: Nothing - } + assertEqual + { actual: filterMap pred (Just 6) + , expected: Just 60 + } + assertEqual + { actual: filterMap pred (Just 5) + , expected: Nothing + } + assertEqual + { actual: filterMap pred Nothing + , expected: Nothing + } - assertEqual { actual: filter (_ > 5) (Just 6) - , expected: Just 6 - } - assertEqual { actual: filter (_ > 5) (Just 5) - , expected: Nothing - } - assertEqual { actual: filter (_ > 5) Nothing - , expected: Nothing - } + assertEqual + { actual: filter (_ > 5) (Just 6) + , expected: Just 6 + } + assertEqual + { actual: filter (_ > 5) (Just 5) + , expected: Nothing + } + assertEqual + { actual: filter (_ > 5) Nothing + , expected: Nothing + } log "Test filterableList instance" *> do let testlist = (1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : Nil) - assertEqual { actual: filterMap pred testlist - , expected: 60 : 70 : 80 : 90 : Nil - } - assertEqual { actual: filter (_ > 5) testlist - , expected: 6 : 7 : 8 : 9 : Nil - } - assertEqual { actual: partition (_ > 5) testlist - , expected: { no: (1 : 2 : 3 : 4 : 5 : Nil), yes: (6 : 7 : 8 : 9 : Nil) } - } - assertEqual { actual: (partitionMap Right $ (1 : 2 : 3 : 4 : 5 : Nil)).right - , expected: 1 : 2 : 3 : 4 : 5 : Nil - } - assertEqual { actual: (partitionMap Left $ (1 : 2 : 3 : 4 : 5 : Nil)).left - , expected: 1 : 2 : 3 : 4 : 5 : Nil - } + assertEqual + { actual: filterMap pred testlist + , expected: 60 : 70 : 80 : 90 : Nil + } + assertEqual + { actual: filter (_ > 5) testlist + , expected: 6 : 7 : 8 : 9 : Nil + } + assertEqual + { actual: partition (_ > 5) testlist + , expected: { no: (1 : 2 : 3 : 4 : 5 : Nil), yes: (6 : 7 : 8 : 9 : Nil) } + } + assertEqual + { actual: (partitionMap Right $ (1 : 2 : 3 : 4 : 5 : Nil)).right + , expected: 1 : 2 : 3 : 4 : 5 : Nil + } + assertEqual + { actual: (partitionMap Left $ (1 : 2 : 3 : 4 : 5 : Nil)).left + , expected: 1 : 2 : 3 : 4 : 5 : Nil + } log "Test filterableArray instance" *> do - assertEqual { actual: filterMap pred [1,2,3,4,5,6,7,8,9] - , expected: [60,70,80,90] - } - assertEqual { actual: filter (_ > 5) [1,2,3,4,5,6,7,8,9] - , expected: [6,7,8,9] - } - assertEqual { actual: partition (_ > 5) [1,2,3,4,5,6,7,8,9] - , expected: { no: [1,2,3,4,5], yes: [6,7,8,9] } - } + assertEqual + { actual: filterMap pred [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] + , expected: [ 60, 70, 80, 90 ] + } + assertEqual + { actual: filter (_ > 5) [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] + , expected: [ 6, 7, 8, 9 ] + } + assertEqual + { actual: partition (_ > 5) [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] + , expected: { no: [ 1, 2, 3, 4, 5 ], yes: [ 6, 7, 8, 9 ] } + } log "Test filterableMap instance" *> do let predE x = if x > 5 then Right (x * 10) else Left x let m = Map.fromFoldable - let xs = m [4 /\ 4, 5 /\ 5, 6 /\ 6, 7 /\ 7] - assertEqual { actual: filterMap pred xs - , expected: m [6 /\ 60, 7 /\ 70] - } - assertEqual { actual: filter (_ > 5) xs - , expected: m [6 /\ 6, 7 /\ 7] - } - assertEqual { actual: partition (_ > 5) xs - , expected: { no: m [4 /\ 4, 5 /\ 5], yes: m [6 /\ 6, 7 /\ 7] } - } - assertEqual { actual: partitionMap predE xs - , expected: { left: m [4 /\ 4, 5 /\ 5], right: m [6 /\ 60, 7 /\ 70] } - } + let xs = m [ 4 /\ 4, 5 /\ 5, 6 /\ 6, 7 /\ 7 ] + assertEqual + { actual: filterMap pred xs + , expected: m [ 6 /\ 60, 7 /\ 70 ] + } + assertEqual + { actual: filter (_ > 5) xs + , expected: m [ 6 /\ 6, 7 /\ 7 ] + } + assertEqual + { actual: partition (_ > 5) xs + , expected: { no: m [ 4 /\ 4, 5 /\ 5 ], yes: m [ 6 /\ 6, 7 /\ 7 ] } + } + assertEqual + { actual: partitionMap predE xs + , expected: { left: m [ 4 /\ 4, 5 /\ 5 ], right: m [ 6 /\ 60, 7 /\ 70 ] } + } where - pred x = if x > 5 then Just (x * 10) else Nothing + pred x = if x > 5 then Just (x * 10) else Nothing testWitherable :: Effect Unit testWitherable = do - log "Test witherableMaybe instance" *> do - assertEqual { actual: map _.right (wilt predE (Just 6)) - , expected: Identity (Just 60) - } - assertEqual { actual: map _.left (wilt predE (Just 5)) - , expected: Identity (Just 5) - } - assertEqual { actual: map _.right (wilt predE Nothing) - , expected: Identity Nothing - } + log "Test witherableMaybe instance" *> do + assertEqual + { actual: map _.right (wilt predE (Just 6)) + , expected: Identity (Just 60) + } + assertEqual + { actual: map _.left (wilt predE (Just 5)) + , expected: Identity (Just 5) + } + assertEqual + { actual: map _.right (wilt predE Nothing) + , expected: Identity Nothing + } - assertEqual { actual: wither predM (Just 6) - , expected: Identity (Just 60) - } - assertEqual { actual: wither predM (Just 5) - , expected: Identity Nothing - } - assertEqual { actual: wither predM Nothing - , expected: Identity Nothing - } + assertEqual + { actual: wither predM (Just 6) + , expected: Identity (Just 60) + } + assertEqual + { actual: wither predM (Just 5) + , expected: Identity Nothing + } + assertEqual + { actual: wither predM Nothing + , expected: Identity Nothing + } - log "Test witherableEither instance" *> do - assertEqual { actual: map _.right (wilt predE (Right 6 :: Either (Array Int) Int)) - , expected: Identity (Right 60) - } - assertEqual { actual: map _.left (wilt predE (Right 5 :: Either (Array Int) Int)) - , expected: Identity (Right 5) - } - assertEqual { actual: map _.right (wilt predE (Left [] :: Either (Array Int) Int)) - , expected: Identity (Left []) - } + log "Test witherableEither instance" *> do + assertEqual + { actual: map _.right (wilt predE (Right 6 :: Either (Array Int) Int)) + , expected: Identity (Right 60) + } + assertEqual + { actual: map _.left (wilt predE (Right 5 :: Either (Array Int) Int)) + , expected: Identity (Right 5) + } + assertEqual + { actual: map _.right (wilt predE (Left [] :: Either (Array Int) Int)) + , expected: Identity (Left []) + } - assertEqual { actual: wither predM (Just 6) - , expected: Identity (Just 60) - } - assertEqual { actual: wither predM (Just 5) - , expected: Identity Nothing - } - assertEqual { actual: wither predM Nothing - , expected: Identity Nothing - } + assertEqual + { actual: wither predM (Just 6) + , expected: Identity (Just 60) + } + assertEqual + { actual: wither predM (Just 5) + , expected: Identity Nothing + } + assertEqual + { actual: wither predM Nothing + , expected: Identity Nothing + } - log "Test witherableList instance" *> do - let testlist = (1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : Nil) - let resultWilt = wilt predE testlist - assertEqual { actual: map _.right resultWilt - , expected: Identity (60 : 70 : 80 : 90 : Nil) - } - assertEqual { actual: map _.left resultWilt - , expected: Identity (1 : 2 : 3 : 4 : 5 : Nil) - } - assertEqual { actual: map _.right (wilt predE Nil) - , expected: Identity Nil - } + log "Test witherableList instance" *> do + let testlist = (1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : Nil) + let resultWilt = wilt predE testlist + assertEqual + { actual: map _.right resultWilt + , expected: Identity (60 : 70 : 80 : 90 : Nil) + } + assertEqual + { actual: map _.left resultWilt + , expected: Identity (1 : 2 : 3 : 4 : 5 : Nil) + } + assertEqual + { actual: map _.right (wilt predE Nil) + , expected: Identity Nil + } - assertEqual { actual: wither predM testlist - , expected: Identity (60 : 70 : 80 : 90 : Nil) - } - assertEqual { actual: wither predM Nil - , expected: Identity Nil - } + assertEqual + { actual: wither predM testlist + , expected: Identity (60 : 70 : 80 : 90 : Nil) + } + assertEqual + { actual: wither predM Nil + , expected: Identity Nil + } - log "Test witherableArray instance" *> do - let testarray = [1, 2, 3, 4, 5, 6, 7, 8, 9] - let resultWilt = wilt predE testarray - assertEqual { actual: map _.right resultWilt - , expected: Identity [60, 70, 80, 90] - } - assertEqual { actual: map _.left resultWilt - , expected: Identity [1, 2, 3, 4, 5] - } - assertEqual { actual: map _.right (wilt predE []) - , expected: Identity [] - } + log "Test witherableArray instance" *> do + let testarray = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] + let resultWilt = wilt predE testarray + assertEqual + { actual: map _.right resultWilt + , expected: Identity [ 60, 70, 80, 90 ] + } + assertEqual + { actual: map _.left resultWilt + , expected: Identity [ 1, 2, 3, 4, 5 ] + } + assertEqual + { actual: map _.right (wilt predE []) + , expected: Identity [] + } - assertEqual { actual: wither predM testarray - , expected: Identity [60, 70, 80, 90] - } - assertEqual { actual: wither predM [], expected: Identity [] } + assertEqual + { actual: wither predM testarray + , expected: Identity [ 60, 70, 80, 90 ] + } + assertEqual { actual: wither predM [], expected: Identity [] } - log "Test witherableMap instance" *> do - let m = Map.fromFoldable - let xs = m [4 /\ 4, 5 /\ 5, 6 /\ 6, 7 /\ 7] - let resultWilt = wilt predE xs - assertEqual { actual: map _.right resultWilt - , expected: Identity (m [6 /\ 60, 7 /\ 70]) - } - assertEqual { actual: map _.left resultWilt - , expected: Identity (m [4 /\ 4, 5 /\ 5]) - } + log "Test witherableMap instance" *> do + let m = Map.fromFoldable + let xs = m [ 4 /\ 4, 5 /\ 5, 6 /\ 6, 7 /\ 7 ] + let resultWilt = wilt predE xs + assertEqual + { actual: map _.right resultWilt + , expected: Identity (m [ 6 /\ 60, 7 /\ 70 ]) + } + assertEqual + { actual: map _.left resultWilt + , expected: Identity (m [ 4 /\ 4, 5 /\ 5 ]) + } - assertEqual { actual: wither predM xs - , expected: Identity (m [6 /\ 60, 7 /\ 70]) - } + assertEqual + { actual: wither predM xs + , expected: Identity (m [ 6 /\ 60, 7 /\ 70 ]) + } - where - predM x = if x > 5 then Identity (Just (x * 10)) else Identity Nothing - predE x = if x > 5 then Identity (Right (x * 10)) else Identity (Left x) + where + predM x = if x > 5 then Identity (Just (x * 10)) else Identity Nothing + predE x = if x > 5 then Identity (Right (x * 10)) else Identity (Left x) main :: Effect Unit main = do