1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
-- |
-- Module : Foundation.Collection.Collection
-- License : BSD-style
-- Maintainer : Foundation
-- Stability : experimental
-- Portability : portable
--
-- Provide basic collection information. It's difficult to provide a
-- unified interface to all sorts of collection, but when creating this
-- API we had the following types in mind:
--
-- * List (e.g [a])
-- * Array
-- * Collection of collection (e.g. deque)
-- * Hashtables, Trees
--
-- an API to rules them all, and in the darkness bind them.
--
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeOperators #-}
module Foundation.Collection.Collection
( Collection(..)
-- * NonEmpty Property
, NonEmpty
, getNonEmpty
, nonEmpty
, nonEmpty_
, nonEmptyFmap
, and
, or
) where
import Basement.Compat.Base hiding (and)
import Basement.Types.OffsetSize
import Basement.Types.AsciiString
import Basement.Exception (NonEmptyCollectionIsEmpty(..))
import Foundation.Collection.Element
import Basement.NonEmpty
import qualified Data.List
import qualified Basement.Block as BLK
import qualified Basement.UArray as UV
import qualified Basement.BoxedArray as BA
import qualified Basement.String as S
-- | Smart constructor to create a NonEmpty collection
--
-- If the collection is empty, then Nothing is returned
-- Otherwise, the collection is wrapped in the NonEmpty property
nonEmpty :: Collection c => c -> Maybe (NonEmpty c)
nonEmpty c
| null c = Nothing
| otherwise = Just (NonEmpty c)
-- | same as 'nonEmpty', but assume that the collection is non empty,
-- and return an asynchronous error if it is.
nonEmpty_ :: Collection c => c -> NonEmpty c
nonEmpty_ c
| null c = throw NonEmptyCollectionIsEmpty
| otherwise = NonEmpty c
nonEmptyFmap :: Functor f => (a -> b) -> NonEmpty (f a) -> NonEmpty (f b)
nonEmptyFmap f (NonEmpty l) = NonEmpty (fmap f l)
-- | A set of methods for ordered colection
class (IsList c, Item c ~ Element c) => Collection c where
{-# MINIMAL null, length, (elem | notElem), minimum, maximum, all, any #-}
-- | Check if a collection is empty
null :: c -> Bool
-- | Length of a collection (number of Element c)
length :: c -> CountOf (Element c)
-- | Check if a collection contains a specific element
--
-- This is the inverse of `notElem`.
elem :: forall a . (Eq a, a ~ Element c) => Element c -> c -> Bool
elem e col = not $ e `notElem` col
-- | Check if a collection does *not* contain a specific element
--
-- This is the inverse of `elem`.
notElem :: forall a . (Eq a, a ~ Element c) => Element c -> c -> Bool
notElem e col = not $ e `elem` col
-- | Get the maximum element of a collection
maximum :: forall a . (Ord a, a ~ Element c) => NonEmpty c -> Element c
-- | Get the minimum element of a collection
minimum :: forall a . (Ord a, a ~ Element c) => NonEmpty c -> Element c
-- | Determine is any elements of the collection satisfy the predicate
any :: (Element c -> Bool) -> c -> Bool
-- | Determine is all elements of the collection satisfy the predicate
all :: (Element c -> Bool) -> c -> Bool
instance Collection [a] where
null = Data.List.null
length = CountOf . Data.List.length
elem = Data.List.elem
notElem = Data.List.notElem
minimum = Data.List.minimum . getNonEmpty
maximum = Data.List.maximum . getNonEmpty
any = Data.List.any
all = Data.List.all
instance UV.PrimType ty => Collection (BLK.Block ty) where
null = (==) 0 . BLK.length
length = BLK.length
elem = BLK.elem
minimum = BLK.foldl1' min
maximum = BLK.foldl1' max
all = BLK.all
any = BLK.any
instance UV.PrimType ty => Collection (UV.UArray ty) where
null = UV.null
length = UV.length
elem = UV.elem
minimum = UV.foldl1' min
maximum = UV.foldl1' max
all = UV.all
any = UV.any
instance Collection (BA.Array ty) where
null = BA.null
length = BA.length
elem = BA.elem
minimum = BA.foldl1' min
maximum = BA.foldl1' max
all = BA.all
any = BA.any
deriving instance Collection AsciiString
instance Collection S.String where
null = S.null
length = S.length
elem = S.elem
minimum = Data.List.minimum . toList . getNonEmpty -- TODO faster implementation
maximum = Data.List.maximum . toList . getNonEmpty -- TODO faster implementation
all = S.all
any = S.any
instance Collection c => Collection (NonEmpty c) where
null _ = False
length = length . getNonEmpty
elem e = elem e . getNonEmpty
maximum = maximum . getNonEmpty
minimum = minimum . getNonEmpty
all p = all p . getNonEmpty
any p = any p . getNonEmpty
-- | Return True if all the elements in the collection are True
and :: (Collection col, Element col ~ Bool) => col -> Bool
and = all (== True)
-- | Return True if at least one element in the collection is True
or :: (Collection col, Element col ~ Bool) => col -> Bool
or = any (== True)
|