packages feed

cherry-core-alpha-0.1.0.0: src/List.hs

{-|

Module      : List
Description : Work with lists.
License     : BSD 3
Maintainer  : terezasokol@gmail.com
Stability   : experimental
Portability : POSIX

-}

module List
  ( -- * Create
    List, singleton, repeat, range

    -- * Transform
  , map, indexedMap, foldl, foldr, filter, filterMap

    -- * Utilities
  , length, reverse, member, all, any, maximum, minimum, sum, product

    -- * Combine
  , append, concat, concatMap, intersperse, map2, map3, map4, map5

    -- * Sort
  , sort, sortBy, sortWith

    -- * Deconstruct
  , isEmpty, head, tail, take, drop, partition, unzip
  )
where

import Prelude (Applicative, Char, Eq, Functor, Monad, Num, Ord, Show, Bool(..), Int, Ordering, (-), flip, mappend, mconcat)
import Maybe (Maybe (..))
import qualified Prelude
import qualified Data.List
import qualified Data.Maybe
import qualified Internal.Shortcut as Shortcut


{-| A list.
-}
type List a = [a]



-- CREATE


{-| Create a list with only one element:

  >  singleton 1234 == [1234]
  >  singleton "hi" == ["hi"]

-}
singleton :: a -> List a
singleton value =
  [value]


{-| Create a list with *n* copies of a value:

  >  repeat 3 (0,0) == [(0,0),(0,0),(0,0)]
-}
repeat :: Int -> a -> List a
repeat =
  Data.List.replicate


{-| Create a list of numbers, every element increasing by one.
You give the lowest and highest number that should be in the list.

  >  range 3 6 == [3, 4, 5, 6]
  >  range 3 3 == [3]
  >  range 6 3 == []
-}
range :: Int -> Int -> List Int
range lo hi =
  [lo .. hi]



-- TRANSFORM


{-| Apply a function to every element of a list.

  >  map sqrt [1,4,9] == [1,2,3]
  >
  >  map not [True,False,True] == [False,True,False]

So `map func [ a, b, c ]` is the same as `[ func a, func b, func c ]`
-}
map :: (a -> b) -> List a -> List b
map =
  Prelude.fmap


{-| Same as `map` but the function is also applied to the index of each
element (starting at zero).

  >  indexedMap Tuple.pair ["Tom","Sue","Bob"] == [ (0,"Tom"), (1,"Sue"), (2,"Bob") ]
-}
indexedMap :: (Int -> a -> b) -> List a -> List b
indexedMap f xs =
  List.map2 f [0 .. (length xs - 1)] xs


{-| Reduce a list from the left.

  >  foldl (+)  0  [1,2,3] == 6
  >  foldl (::) [] [1,2,3] == [3,2,1]

So 'foldl step state [1,2,3]' is like saying:

  >  state
  >    |> step 1
  >    |> step 2
  >    |> step 3
-}
foldl :: (a -> b -> b) -> b -> List a -> b
foldl func =
  -- Note: This function is implemented using fold' to eagerly evaluate the
  -- accumulator, preventing space leaks.
  Data.List.foldl' (flip func)


{-| Reduce a list from the right.

  >  foldr (+)  0  [1,2,3] == 6
  >  foldr (::) [] [1,2,3] == [1,2,3]

So `foldr step state [1,2,3]` is like saying:

  >  state
  >    |> step 3
  >    |> step 2
  >    |> step 1

-}
foldr :: (a -> b -> b) -> b -> List a -> b
foldr =
  Data.List.foldr


{-| Keep elements that satisfy the test.

  >  filter isEven [1,2,3,4,5,6] == [2,4,6]
-}
filter :: (a -> Bool) -> List a -> List a
filter =
  Data.List.filter


{-| Filter out certain values. For example, maybe you have a bunch of strings
from an untrusted source and you want to turn them into numbers:

  >  numbers :: List Int
  >  numbers =
  >    filterMap String.toInt ["3", "hi", "12", "4th", "May"]
  >
  >  -- numbers == [3, 12]

-}
filterMap :: (a -> Maybe b) -> List a -> List b
filterMap toMaybe =
  Data.Maybe.mapMaybe (\a -> toHMaybe (toMaybe a))



-- UTILITIES


{-| Determine the length of a list.

  >  length [1,2,3] == 3
-}
length :: List a -> Int
length =
  Data.List.length


{-| Reverse a list.

  >  reverse [1,2,3,4] == [4,3,2,1]
-}
reverse :: List a -> List a
reverse =
  Data.List.reverse


{-| Figure out whether a list contains a value.

  >  member 9 [1,2,3,4] == False
  >  member 4 [1,2,3,4] == True
-}
member :: Prelude.Eq a => a -> List a -> Bool
member =
  Data.List.elem

{-| Determine if all elements satisfy some test.

  >  all isEven [2,4] == True
  >  all isEven [2,3] == False
  >  all isEven [] == True
-}
all :: (a -> Bool) -> List a -> Bool
all =
  Data.List.all


{-| Determine if any elements satisfy some test.

  >  any isEven [2,3] == True
  >  any isEven [1,3] == False
  >  any isEven [] == False
-}
any :: (a -> Bool) -> List a -> Bool
any =
  Data.List.any


{-| Find the maximum element in a non-empty list.

  >  maximum [1,4,2] == Just 4
  >  maximum []      == Nothing
-}
maximum :: Prelude.Ord a => List a -> Maybe a
maximum list =
  case list of
    [] ->
      Nothing

    _ ->
      Just (Data.List.maximum list)


{-| Find the minimum element in a non-empty list.

  >  minimum [3,2,1] == Just 1
  >  minimum []      == Nothing
-}
minimum :: Prelude.Ord a => List a -> Maybe a
minimum list =
  case list of
    [] ->
      Nothing

    _ ->
      Just (Data.List.minimum list)


{-| Get the sum of the list elements.

  >  sum [1,2,3] == 6
  >  sum [1,1,1] == 3
  >  sum []      == 0

-}
sum :: Prelude.Num a => List a -> a
sum =
  Prelude.sum


{-| Get the product of the list elements.

  >  product [2,2,2] == 8
  >  product [3,3,3] == 27
  >  product []      == 1

-}
product :: Prelude.Num a => List a -> a
product =
  Prelude.product



-- COMBINE


{-| Put two lists together.

  >  append [1,1,2] [3,5,8] == [1,1,2,3,5,8]
  >  append ['a','b'] ['c'] == ['a','b','c']

You can also use [the `(++)` operator](Basics#++) to append lists.
-}
append :: List a -> List a -> List a
append =
  Prelude.mappend


{-| Concatenate a bunch of lists into a single list:

  >  concat [[1,2],[3],[4,5]] == [1,2,3,4,5]
-}
concat :: List (List a) -> List a
concat =
  Prelude.mconcat


{-| Map a given function onto a list and flatten the resulting lists.

  >  concatMap f xs == concat (map f xs)
-}
concatMap :: (a -> List b) -> List a -> List b
concatMap =
  Shortcut.andThen


{-| Places the given value between all members of the given list.

  >  intersperse "on" ["turtles","turtles","turtles"] == ["turtles","on","turtles","on","turtles"]
-}
intersperse :: a -> List a -> List a
intersperse =
  Data.List.intersperse


{-| Combine two lists, combining them with the given function.
If one list is longer, the extra elements are dropped.

  >  totals :: List Int -> List Int -> List Int
  >  totals xs ys =
  >    List.map2 (+) xs ys
  >
  >  -- totals [1,2,3] [4,5,6] == [5,7,9]
  >
  >  pairs :: List a -> List b -> List (a,b)
  >  pairs xs ys =
  >    List.map2 Tuple.pair xs ys
  >
  >  -- pairs ["alice","bob","chuck"] [2,5,7,8]
  >  --   == [("alice",2),("bob",5),("chuck",7)]

-}
map2 :: (a -> b -> result) -> List a -> List b -> List result
map2 =
  Data.List.zipWith


{-| -}
map3 :: (a -> b -> c -> result) -> List a -> List b -> List c -> List result
map3 =
  Data.List.zipWith3


{-| -}
map4 :: (a -> b -> c -> d -> result) -> List a -> List b -> List c -> List d -> List result
map4 =
  Data.List.zipWith4


{-| -}
map5 :: (a -> b -> c -> d -> e -> result) -> List a -> List b -> List c -> List d -> List e -> List result
map5 =
  Data.List.zipWith5



-- SORT


{-| Sort values from lowest to highest

    sort [3,1,5] == [1,3,5]
-}
sort :: Prelude.Ord a => List a -> List a
sort =
  Data.List.sort


{-| Sort values by a derived property.

  >  alice = { name="Alice", height=1.62 }
  >  bob   = { name="Bob"  , height=1.85 }
  >  chuck = { name="Chuck", height=1.76 }
  >
  >  sortBy .name   [chuck,alice,bob] == [alice,bob,chuck]
  >  sortBy .height [chuck,alice,bob] == [alice,chuck,bob]
  >
  >  sortBy String.length ["mouse","cat"] == ["cat","mouse"]
-}
sortBy :: Prelude.Ord b => (a -> b) -> List a -> List a
sortBy =
  Data.List.sortOn


{-| Sort values with a custom comparison function.

  >  sortWith flippedComparison [1,2,3,4,5] == [5,4,3,2,1]
  >
  >  flippedComparison a b =
  >      case compare a b of
  >        LT -> GT
  >        EQ -> EQ
  >        GT -> LT

This is also the most general sort function, allowing you
to define any other: `sort == sortWith compare`
-}
sortWith :: (a -> a -> Ordering) -> List a -> List a
sortWith =
  Data.List.sortBy



-- DECONSTRUCT


{-| Determine if a list is empty.

  >  isEmpty [] == True

Note: It is usually preferable to use a `case` to test this so you do not
forget to handle the `(x :: xs)` case as well!
-}
isEmpty :: List a -> Bool
isEmpty =
  Data.List.null


{-| Extract the first element of a list.

  >  head [1,2,3] == Just 1
  >  head [] == Nothing

Note: It is usually preferable to use a `case` to deconstruct a `List`
because it gives you `(x :: xs)` and you can work with both subparts.
-}
head :: List a -> Maybe a
head xs =
  case xs of
    x : _ ->
      Just x

    [] ->
      Nothing


{-| Extract the rest of the list.

  >  tail [1,2,3] == Just [2,3]
  >  tail [] == Nothing

Note: It is usually preferable to use a `case` to deconstruct a `List`
because it gives you `(x :: xs)` and you can work with both subparts.
-}
tail :: List a -> Maybe (List a)
tail list =
  case list of
    _ : xs ->
      Just xs

    [] ->
      Nothing


{-| Take the first *n* members of a list.

  >  take 2 [1,2,3,4] == [1,2]
-}
take :: Int -> List a -> List a
take =
  Data.List.take


{-| Drop the first *n* members of a list.

  >  drop 2 [1,2,3,4] == [3,4]
-}
drop :: Int -> List a -> List a
drop =
  Data.List.drop


{-| Partition a list based on some test. The first list contains all values
that satisfy the test, and the second list contains all the value that do not.

  >  partition (\x -> x < 3) [0,1,2,3,4,5] == ([0,1,2], [3,4,5])
  >  partition isEven        [0,1,2,3,4,5] == ([0,2,4], [1,3,5])
-}
partition :: (a -> Bool) -> List a -> (List a, List a)
partition =
  Data.List.partition


{-| Decompose a list of tuples into a tuple of lists.

  >  unzip [(0, True), (17, False), (1337, True)] == ([0,17,1337], [True,False,True])
-}
unzip :: List (a, b) -> (List a, List b)
unzip =
  Data.List.unzip



-- INTERNAL


toHMaybe :: Maybe a -> Data.Maybe.Maybe a
toHMaybe maybe =
  case maybe of
    Just a -> Data.Maybe.Just a
    Nothing -> Data.Maybe.Nothing