module Data.List.Safe where

import Data.Bifunctor (first)


-- | Analogous to 'Data.List.last', but returns 'Nothing' on an empty list, instead of throwing an error.
safeLast :: [a] -> Maybe a
safeLast :: [a] -> Maybe a
safeLast [] = Maybe a
forall a. Maybe a
Nothing
safeLast [a]
l  = a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ [a] -> a
forall a. [a] -> a
last [a]
l

-- | Analogous to `Data.List.head`, but returns 'Nothing' in case of an empty list.
safeHead :: [a] -> Maybe a
safeHead :: [a] -> Maybe a
safeHead []      = Maybe a
forall a. Maybe a
Nothing
safeHead (a
x : [a]
_) = a -> Maybe a
forall a. a -> Maybe a
Just a
x

-- | Analogous tu 'Data.List.!!', but does not throw an error on missing index.
safeIndex :: Int -> [a] -> Maybe a
safeIndex :: Int -> [a] -> Maybe a
safeIndex Int
_ []       = Maybe a
forall a. Maybe a
Nothing
safeIndex Int
0 (a
x : [a]
_)  = a -> Maybe a
forall a. a -> Maybe a
Just a
x
safeIndex Int
n (a
_ : [a]
xs)
  | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0            = Maybe a
forall a. Maybe a
Nothing
  | Bool
otherwise        = Int -> [a] -> Maybe a
forall a. Int -> [a] -> Maybe a
safeIndex (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) [a]
xs

-- | Safely deconstructs a list from the end.
--
--   More efficient than @(init x, last x)@
safeUnsnoc :: [a] -> Maybe ([a], a)
safeUnsnoc :: [a] -> Maybe ([a], a)
safeUnsnoc []       = Maybe ([a], a)
forall a. Maybe a
Nothing
safeUnsnoc [a
x]      = ([a], a) -> Maybe ([a], a)
forall a. a -> Maybe a
Just ([], a
x)
safeUnsnoc (a
x : [a]
xs) = ([a] -> [a]) -> ([a], a) -> ([a], a)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (a
x a -> [a] -> [a]
forall a. a -> [a] -> [a]
:) (([a], a) -> ([a], a)) -> Maybe ([a], a) -> Maybe ([a], a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [a] -> Maybe ([a], a)
forall a. [a] -> Maybe ([a], a)
safeUnsnoc [a]
xs

-- | Safely deconstructs a list from the beginning, returning 'Nothing' if the list is empty.
safeUncons :: [a] -> Maybe (a, [a])
safeUncons :: [a] -> Maybe (a, [a])
safeUncons []       = Maybe (a, [a])
forall a. Maybe a
Nothing
safeUncons (a
x : [a]
xs) = (a, [a]) -> Maybe (a, [a])
forall a. a -> Maybe a
Just (a
x, [a]
xs)