packages feed

either-list-functions 0.0.2.0 → 0.0.4.0

raw patch · 4 files changed

+284/−64 lines, 4 filesdep +containersdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: containers

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Data.List.EitherFunctions: branchLeft :: BranchComparison a -> [Either a b] -> ([b], Forest (a, [b]))
+ Data.List.EitherFunctions: branchRight :: BranchComparison b -> [Either a b] -> ([a], Forest (b, [a]))
+ Data.List.EitherFunctions: leadLeft :: [Either a b] -> ([b], [(a, [b])])
+ Data.List.EitherFunctions: leadLeft' :: a -> [Either a b] -> [(a, [b])]
+ Data.List.EitherFunctions: leadRight :: [Either a b] -> ([a], [(b, [a])])
+ Data.List.EitherFunctions: leadRight' :: b -> [Either a b] -> [(b, [a])]
+ Data.List.EitherFunctions: spanLeft' :: [Either a b] -> ([a], Maybe (b, [Either a b]))
+ Data.List.EitherFunctions: spanRight' :: [Either a b] -> ([b], Maybe (a, [Either a b]))
+ Data.List.EitherFunctions: type BranchComparison a = Comparison a

Files

changelog.txt view
@@ -1,3 +1,15 @@+0.0.4.0+  - Added:+      - spanLeft'+      - spanRight'+      - leadLeft+      - leadLeft'+      - leadRight+      - leadRight'+      - branchLeft+      - branchRight+      - BranchComparison+ 0.0.2.0   - Supports base-4.12   - Added 'partition' function
either-list-functions.cabal view
@@ -1,5 +1,5 @@ name:     either-list-functions-version:  0.0.2.0+version:  0.0.4.0 category: Data synopsis: Functions involving lists of Either @@ -28,12 +28,14 @@   default-language: Haskell2010   hs-source-dirs: src   ghc-options: -Wall+  exposed-modules: Data.List.EitherFunctions+  build-depends: base, containers -  build-depends:-      base >=4.9 && <4.13+  -- require Data.Functor.Contravariant+  build-depends: base >=4.12 -  exposed-modules:-      Data.List.EitherFunctions+  -- upper bounds to exclude untested versions+  build-depends: base <4.13, containers <0.7  test-suite doctest   default-language: Haskell2010@@ -41,8 +43,9 @@   main-is: doctest.hs   ghc-options: -Wall -threaded   hs-source-dirs: test+  build-depends: base, doctest, either-list-functions -  build-depends:-      base >=4.9 && <4.13-    , doctest-    , either-list-functions+  build-depends: base >=4.12++  -- upper bounds to exclude untested versions+  build-depends: base <4.13
license.txt view
@@ -1,4 +1,4 @@-Copyright 2017 Chris Martin+Copyright 2017-2020 Chris Martin  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
src/Data/List/EitherFunctions.hs view
@@ -1,80 +1,285 @@-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE BlockArguments, LambdaCase, NoImplicitPrelude #-} -module Data.List.EitherFunctions-  ( partlyMap-  , groupEither-  , spanLeft-  , spanRight-  , partition-  ) where+-- | Functions involving lists of 'Either'. -import Data.Either (Either (..))-import Data.Maybe  (Maybe (..), maybe)+module Data.List.EitherFunctions+  ( -import qualified Data.List as List+    {- * Map       -}  partlyMap,+    {- * Group     -}  groupEither,+    {- * Partition -}  partition,+    {- * Span      -}  spanLeft, spanLeft', spanRight, spanRight',+    {- * Lead      -}  leadLeft, leadLeft', leadRight, leadRight',+    {- * Branch    -}  branchLeft, branchRight, BranchComparison -{- |+  ) where ->>> import Prelude (even, show)+import Data.Bool                   ( Bool (..) )+import Data.Either                 ( Either (..) )+import Data.Function               ( fix )+import Data.Functor.Contravariant  ( Comparison (..), contramap )+import Data.List                   ( foldr, map, span )+import Data.Tree                   ( Tree (..), Forest )+import Data.Maybe                  ( Maybe (..), maybe )+import Data.Ord                    ( Ordering (..) ) ->>> partlyMap (\x -> if even x then Just (show x) else Nothing) [1..5]-[Left 1,Right "2",Left 3,Right "4",Left 5]+-- |+-- >>> import Prelude (even, show)+--+-- >>> partlyMap (\x -> if even x then Just (show x) else Nothing) [1..5]+-- [Left 1,Right "2",Left 3,Right "4",Left 5] --} partlyMap :: (a -> Maybe b) -> [a] -> [Either a b]-partlyMap f = List.map (\x -> maybe (Left x) Right (f x)) -{- |+partlyMap f = map \x -> maybe (Left x) Right (f x) ->>> groupEither [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']-[Left [1,2],Right "a",Left [3],Right "bc"]+-- |+-- >>> groupEither [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']+-- [Left [1,2],Right "a",Left [3],Right "bc"] --} groupEither :: [Either a b] -> [Either [a] [b]]-groupEither [] = []-groupEither ((Left x) : xs)  = let (ys, zs) = spanLeft xs-                               in  Left (x : ys) : groupEither zs-groupEither ((Right x) : xs) = let (ys, zs) = spanRight xs-                               in  Right (x : ys) : groupEither zs -{- |+groupEither = fix \r -> \case+    []            ->  []+    Left  x : xs  ->  Left  (x : ys) : r zs  where (ys, zs) = spanLeft  xs+    Right x : xs  ->  Right (x : ys) : r zs  where (ys, zs) = spanRight xs ->>> spanLeft [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']-([1,2],[Right 'a',Left 3,Right 'b',Right 'c'])+-- |+-- >>> leadLeft [Right 'a', Right 'b', Left 1, Right 'c', Right 'd', Left 2, Right 'e', Right 'f']+-- ("ab",[(1,"cd"),(2,"ef")])+--+-- >>> leadLeft [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']+-- ("",[(1,""),(2,"a"),(3,"bc")]) ->>> spanLeft [Right 'a', Left 3, Right 'b', Right 'c']-([],[Right 'a',Left 3,Right 'b',Right 'c'])+leadLeft :: [Either a b] -> ([b], [(a, [b])]) --}+leadLeft = f+  where+    f xs = (unledItems, ledGroups)+      where+        (unledItems, ysMaybe) = spanRight' xs+        ledGroups = case ysMaybe of+            Nothing            ->  []+            Just (leader, ys)  ->  r leader ys++    r leader xs = firstGroup : moreGroups+      where+        firstGroup = (leader, followers)+        (followers, ysMaybe) = spanRight' xs+        moreGroups = case ysMaybe of+            Nothing             ->  []+            Just (leader', ys)  ->  r leader' ys++-- |+-- >>> leadLeft' 0 [Right 'a', Right 'b', Left 1, Right 'c', Right 'd', Left 2, Right 'e', Right 'f']+-- [(0,"ab"),(1,"cd"),(2,"ef")]+--+-- >>> leadLeft' 0 [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']+-- [(1,""),(2,"a"),(3,"bc")]++leadLeft' ::+    a -- ^ Leader to use for the first group in case the list does not begin with a 'Left'.+    -> [Either a b] -> [(a, [b])]++leadLeft' leader xs = addMissingLeader leader (leadLeft xs)++-- |+-- >>> leadRight [Left 1, Left 2, Right 'a', Left 3, Left 4, Right 'b', Left 5, Left 6]+-- ([1,2],[('a',[3,4]),('b',[5,6])])+--+-- >>> leadRight [Right 'a', Left 3, Left 4, Right 'b', Right 'c', Left 5, Left 6]+-- ([],[('a',[3,4]),('b',[]),('c',[5,6])])++leadRight :: [Either a b] -> ([a], [(b, [a])])++leadRight = f+  where+    f xs = (unledItems, ledGroups)+      where+        (unledItems, ysMaybe) = spanLeft' xs+        ledGroups = case ysMaybe of+            Nothing            ->  []+            Just (leader, ys)  ->  r leader ys++    r leader xs = firstGroup : moreGroups+      where+        firstGroup = (leader, followers)+        (followers, ysMaybe) = spanLeft' xs+        moreGroups = case ysMaybe of+            Nothing             ->  []+            Just (leader', ys)  ->  r leader' ys++-- |+-- >>> leadRight' 'z' [Left 1, Left 2, Right 'a', Left 3, Left 4, Right 'b', Left 5, Left 6]+-- [('z',[1,2]),('a',[3,4]),('b',[5,6])]+--+-- >>> leadRight' 'z' [Right 'a', Left 3, Left 4, Right 'b', Right 'c', Left 5, Left 6]+-- [('a',[3,4]),('b',[]),('c',[5,6])]++leadRight' ::+    b -- ^ Leader to use for the first group in case the list does not begin with a 'Right'.+    -> [Either a b] -> [(b, [a])]++leadRight' leader xs = addMissingLeader leader (leadRight xs)++addMissingLeader :: a -> ([b], [(a, [b])]) -> [(a, [b])]++addMissingLeader _      ( []         , groups ) =                        groups+addMissingLeader leader ( unledIntro , groups ) = (leader, unledIntro) : groups++-- |+-- >>> spanLeft [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']+-- ([1,2],[Right 'a',Left 3,Right 'b',Right 'c'])+--+-- >>> spanLeft [Right 'a', Left 3, Right 'b', Right 'c']+-- ([],[Right 'a',Left 3,Right 'b',Right 'c'])+ spanLeft :: [Either a b] -> ([a], [Either a b])-spanLeft [] = ([], [])-spanLeft ((Left x) : xs) = let (ys, zs) = spanLeft xs-                           in  (x : ys, zs)-spanLeft xs = ([], xs) -{- |+spanLeft = fix \r -> \case+    []           ->  ( []     , [] )+    Left x : xs  ->  ( x : ys , zs )  where (ys, zs) = r xs+    xs           ->  ( []     , xs ) ->>> spanRight [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']-("",[Left 1,Left 2,Right 'a',Left 3,Right 'b',Right 'c'])+-- | Similar to 'spanLeft', but preserves a little more information in the return type: if the remainder of the list is non-empty, then it necessarily begins with a 'Right', and so we can go ahead and unwrap that and return it as a value of type `b`.+--+-- >>> spanLeft' [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']+-- ([1,2],Just ('a',[Left 3,Right 'b',Right 'c']))+--+-- >>> spanLeft' [Right 'a', Left 3, Right 'b', Right 'c']+-- ([],Just ('a',[Left 3,Right 'b',Right 'c']))+--+-- >>> spanLeft' [Left 1, Left 2, Left 3]+-- ([1,2,3],Nothing) ->>> spanRight [Right 'a', Left 3, Right 'b', Right 'c']-("a",[Left 3,Right 'b',Right 'c'])+spanLeft' :: [Either a b] -> ([a], Maybe (b, [Either a b])) --}+spanLeft' = fix \r -> \case+    []            ->  ( []     , Nothing      )+    Left  x : xs  ->  ( x : ys , zs           )  where (ys, zs) = r xs+    Right x : xs  ->  ( []     , Just (x, xs) )++-- | Similar to 'spanRight', but preserves a little more information in the return type: if the remainder of the list is non-empty, then it necessarily begins with a 'Left', and so we can go ahead and unwrap that and return it as a value of type `a`.+--+-- >>> spanRight [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']+-- ("",[Left 1,Left 2,Right 'a',Left 3,Right 'b',Right 'c'])+--+-- >>> spanRight [Right 'a', Left 3, Right 'b', Right 'c']+-- ("a",[Left 3,Right 'b',Right 'c'])+ spanRight :: [Either a b] -> ([b], [Either a b])-spanRight [] = ([], [])-spanRight ((Right x) : xs) = let (ys, zs) = spanRight xs-                             in  (x : ys, zs)-spanRight xs = ([], xs) -{- |+spanRight = fix \r -> \case+    []            ->  ( []     , [] )+    Right x : xs  ->  ( x : ys , zs )  where (ys, zs) = r xs+    xs            ->  ( []     , xs ) ->>> partition [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']-([1,2,3],"abc")+-- |+-- >>> spanRight' [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']+-- ("",Just (1,[Left 2,Right 'a',Left 3,Right 'b',Right 'c']))+--+-- >>> spanRight' [Right 'a', Left 3, Right 'b', Right 'c']+-- ("a",Just (3,[Right 'b',Right 'c']))+--+-- >>> spanRight' [Right 'a', Right 'b', Right 'c']+-- ("abc",Nothing) --}+spanRight' :: [Either a b] -> ([b], Maybe (a, [Either a b]))++spanRight' = fix \r -> \case+    []            ->  ( []     , Nothing      )+    Right x : xs  ->  ( x : ys , zs           )  where (ys, zs) = r xs+    Left  x : xs  ->  ( []     , Just (x, xs) )++-- |+-- >>> partition [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']+-- ([1,2,3],"abc")+ partition :: [Either a b] -> ([a], [b])-partition [] = ([], [])-partition (x : xs) = let (as, bs) = partition xs-                     in  case x of Left a  -> (a : as, bs)-                                   Right b -> (as, b : bs)++partition = fix \r -> \case+    []            ->  ( []     , []     )+    Left  a : xs  ->  ( a : as , bs     )  where (as, bs) = r xs+    Right b : xs  ->  ( as     , b : bs )  where (as, bs) = r xs++-- | The relative significance of branches (greater values are closer to the root).+type BranchComparison a = Comparison a++-- |+-- >>> import Prelude+--+-- >>> heading level title = Left (level, title)+-- >>> chapter = heading 1+-- >>> section = heading 2+-- >>> p text = Right text+--+-- >>> :{+-- >>> list =+-- >>>     [ p "Copyright"+-- >>>     , p "Preface"+-- >>>     , chapter "Animals"+-- >>>     , p "The kingdom animalia"+-- >>>     , section "Vertibrates"+-- >>>     , p "Cats"+-- >>>     , p "Snakes"+-- >>>     , section "Invertibrates"+-- >>>     , p "Worms"+-- >>>     , p "Jellyfishes"+-- >>>     , chapter "Fungus"+-- >>>     , p "Yeast"+-- >>>     , p "Truffles"+-- >>>     , p "Morels"+-- >>>     ]+-- >>> :}+--+-- >>> import Data.Functor.Contravariant+-- >>> flipComparison (Comparison f) = Comparison (flip f)+-- >>> headingComparison = contramap fst (flipComparison defaultComparison)+--+-- >>> (frontMatter, mainMatter) = branchLeft headingComparison list+--+-- >>> frontMatter+-- ["Copyright","Preface"]+--+-- >>> import Data.List+-- >>> showContent ((_, x), ys) = x ++ ": " ++ intercalate ", " ys+--+-- >>> import Data.Tree+-- >>> putStrLn $ drawForest $ map (fmap showContent) mainMatter+-- Animals: The kingdom animalia+-- |+-- +- Vertibrates: Cats, Snakes+-- |+-- `- Invertibrates: Worms, Jellyfishes+-- <BLANKLINE>+-- Fungus: Yeast, Truffles, Morels+-- <BLANKLINE>+-- <BLANKLINE>++branchLeft :: BranchComparison a -> [Either a b] -> ([b], Forest (a, [b]))++branchLeft c xs = (rejects, forest)+  where+    (rejects, nodes) = leadLeft xs+    forest = makeForest c' nodes+    c' = contramap (\(x, _) -> x) c++-- | Same as 'branchLeft', but with the types flipped; here, 'Right' is the case that indicates a branch.++branchRight :: BranchComparison b -> [Either a b] -> ([a], Forest (b, [a]))++branchRight c xs = (rejects, forest)+  where+    (rejects, nodes) = leadRight xs+    forest = makeForest c' nodes+    c' = contramap (\(x, _) -> x) c++makeForest :: BranchComparison a -> [a] -> Forest a++makeForest c = foldr f []+  where+    f x xs = Node x chomped : remainder+      where+        (chomped, remainder) = span (\(Node y _) -> x > y) xs++    x > y = case getComparison c x y of GT -> True; _ -> False