non-empty-zipper 0.1.0.3 → 0.1.0.4
raw patch · 2 files changed
+54/−4 lines, 2 files
Files
- Data/List/NonEmptyZipper.hs +53/−0
- non-empty-zipper.cabal +1/−4
Data/List/NonEmptyZipper.hs view
@@ -18,15 +18,19 @@ module Data.List.NonEmptyZipper where ++import Data.List as List import Data.List.NonEmpty (NonEmpty (..)) import Data.Semigroup + -- | A Zipper with a "current element". The current element represent a singular, -- focus on a single element in a list. So @NonEmptyZipper [1,2] 3 [4]@ is -- roughly eqivelant to @[1,2,3,4]@, where @3@ is the current element. This is -- useful, since we are now guarenteed that the current element is an element in -- the list. data NonEmptyZipper a = NonEmptyZipper+ -- ^ There is only one constructor. { _before :: ![a] -- ^ A list of elements preceeding the current element. , _current :: !a@@ -35,29 +39,35 @@ -- ^ A list of element succeeding the current element. deriving (Show, Eq, Ord) + -- | Map to @_before@, useful as a lens before :: Functor f => ([a] -> f [a]) -> NonEmptyZipper a -> f (NonEmptyZipper a) before f x = (\c' -> x { _before = c' }) <$> f (_before x) + -- | Map to @current@, useful as a lens current :: Functor f => (a -> f a) -> NonEmptyZipper a -> f (NonEmptyZipper a) current f x = (\c' -> x { _current = c' }) <$> f (_current x) + -- | Map to @after@, useful as a lens after :: Functor f => ([a] -> f [a]) -> NonEmptyZipper a -> f (NonEmptyZipper a) after f x = (\c' -> x { _after = c' }) <$> f (_after x) + -- | Its a Functor instance Functor NonEmptyZipper where fmap f (NonEmptyZipper xs y zs) = NonEmptyZipper (f <$> xs) (f y) (f <$> zs) + -- | Its Applicative, but @pure@ might not be what you expect instance Applicative NonEmptyZipper where pure x = NonEmptyZipper [x] x [x] NonEmptyZipper fxs fy fzs <*> NonEmptyZipper xs y zs = NonEmptyZipper (fxs <*> xs) (fy y) (fzs <*> zs) + -- | Advance the current element forward by one. -- If the current is the last element in the list, we do nothing. next :: NonEmptyZipper a -> NonEmptyZipper a@@ -65,6 +75,7 @@ NonEmptyZipper (xs <> [y]) z zs next z = z + -- | Advance the current element forward by one. -- If the current is the last element in the list, we loop back to the first. nextMod :: NonEmptyZipper a -> NonEmptyZipper a@@ -72,6 +83,7 @@ NonEmptyZipper [] x (xs <> [y]) nextMod z = next z + -- | Move the current element backward by one. -- If the current is the first element in the list, we do nothing. previous :: NonEmptyZipper a -> NonEmptyZipper a@@ -79,6 +91,7 @@ NonEmptyZipper (Prelude.init xs) (Prelude.last xs) (y:zs) previous z = z + -- | Move the current element backward by one. -- If the current is the first element in the list, we loop to the last element. previousMod :: NonEmptyZipper a -> NonEmptyZipper a@@ -86,44 +99,53 @@ NonEmptyZipper (y:Prelude.init zs) (Prelude.last zs) [] previousMod z = previous z + -- | Convert to a standard list. Current element information will be lost. toList :: NonEmptyZipper a -> [a] toList (NonEmptyZipper xs y zs) = xs <> (y:zs) + -- | Convert from @NonEmpty@. The first element will be the current element. fromNonEmpty :: NonEmpty a -> NonEmptyZipper a fromNonEmpty (x :| xs) = NonEmptyZipper [] x xs + -- | Is the current selection the first item in the collection? inTheBeginning :: NonEmptyZipper a -> Bool inTheBeginning (NonEmptyZipper [] _ _) = True inTheBeginning _ = False + -- | Is the current selection the last item in the collection? inTheEnd :: NonEmptyZipper a -> Bool inTheEnd (NonEmptyZipper _ _ []) = True inTheEnd _ = False + -- | Get the index of the current element in the collection. getPosition :: NonEmptyZipper a -> Int getPosition (NonEmptyZipper xs _ _) = Prelude.length xs + -- | Measure the size of the collection. Will be atleast 1. length :: NonEmptyZipper a -> Int length (NonEmptyZipper xs _ zs) = Prelude.length xs + 1 + Prelude.length zs + -- | Its a @Semigroup@ by appending the second -- @NonEmptyZipper@ to the @_after@ list. instance Semigroup (NonEmptyZipper a) where NonEmptyZipper xs y zs <> z = NonEmptyZipper xs y $ zs <> toList z + -- | Get the first element out of the NonEmptyZipper. head :: NonEmptyZipper a -> a head = \case NonEmptyZipper [] x _ -> x NonEmptyZipper (x:_) _ _ -> x + -- | Get all elements out of the NonEmptyZipper that are not the last element. -- If there is only one element in the collection, it's @Nothing@. init :: NonEmptyZipper a -> Maybe (NonEmptyZipper a)@@ -134,12 +156,14 @@ NonEmptyZipper xs y zs -> Just $ NonEmptyZipper xs y (Prelude.init zs) + -- | Get the last element out of the NonEmptyZipper. last :: NonEmptyZipper a -> a last = \case NonEmptyZipper _ x [] -> x NonEmptyZipper _ _ xs -> Prelude.last xs + -- | Get all elements out of the NonEmptyZipper that are not the first element. -- If there is only one element in the collection, its @Nothing@. tail :: NonEmptyZipper a -> Maybe (NonEmptyZipper a)@@ -148,16 +172,45 @@ NonEmptyZipper (_:xs) y zs -> Just $ NonEmptyZipper xs y zs NonEmptyZipper _ _ (z:zs) -> Just $ NonEmptyZipper [] z zs + -- | Flip the NonEmptyZipper, maintaining the current element. reverse :: NonEmptyZipper a -> NonEmptyZipper a reverse (NonEmptyZipper xs y zs) = NonEmptyZipper (Prelude.reverse zs) y (Prelude.reverse xs) + -- | Add one element to the front of a NonEmptyZipper. cons :: a -> NonEmptyZipper a -> NonEmptyZipper a cons x (NonEmptyZipper xs y zs) = NonEmptyZipper (x:xs) y zs + -- | This is like @pure@ but more intuitive. -- The Applicative instance for @NonEmptyZipper@ is likely not what you expect. wrap :: a -> NonEmptyZipper a wrap x = NonEmptyZipper [] x []+++-- | Its Foldable+instance Foldable NonEmptyZipper where+ foldr f i (NonEmptyZipper xs y zs) =+ foldr f (foldr f (foldr f i zs) [y]) xs+++-- | Set the current element in the NonEmptyZipper.+-- If the element does not appear in the collection,+-- the result with be @Nothing@+setCurrent :: Eq a => a -> NonEmptyZipper a -> Maybe (NonEmptyZipper a)+setCurrent x xs | x `elem` xs =+ let (as, bs) = List.break (== x) $ toList xs+ in Just $ NonEmptyZipper (List.delete x as) x (List.delete x bs)+setCurrent _ _ = Nothing+++-- | Set the current element in the NonEmptyZipper via index location.+-- If the index is out of bounds the results will be @Nothing@.+setCurrentIndex :: Int -> NonEmptyZipper a -> Maybe (NonEmptyZipper a)+setCurrentIndex i xs+ | List.length xs > i && i >= 0 =+ let (bs, x:as) = splitAt i $ toList xs in+ Just $ NonEmptyZipper bs x as+setCurrentIndex _ _ = Nothing
non-empty-zipper.cabal view
@@ -1,8 +1,5 @@--- Initial NonEmptyZipper.cabal generated by cabal init. For further--- documentation, see http://haskell.org/cabal/users-guide/- name: non-empty-zipper-version: 0.1.0.3+version: 0.1.0.4 synopsis: The Zipper for NonEmpty description: The Zipper for NonEmpty. Useful for things like tabs, button groups, and slideshows. Basically any case in which