-- SPDX-License-Identifier: MIT
-- Copyright (c) 2020 Chua Hou

-----------------------------------------------------------------------------
-- |
-- Module      : Delude
-- Copyright   : Chua Hou 2020
-- License     : MIT
--
-- Maintainer  : Chua Hou <human+github@chuahou.dev>
-- Stability   : experimental
-- Portability : non-portable
--
-- A simple @Prelude@ replacement, aiming to bring unsafe partial functions out
-- of scope, and bring in replacements work as suitable replacements, such as
-- @readMaybe@ instead of @read@, and @head@/@tail@ that return @Maybe@ values.
-----------------------------------------------------------------------------

module Delude (
              -- * Rewritten functions
              -- ** Folds and traversals
                foldr1
              , foldl1
              , foldr1'
              , foldl1'
              , maximum
              , minimum
              , maximum'
              , minimum'
              , sum
              , product
              -- ** List operations
              , head
              , last
              , tail
              , init
              , (!?)
              -- * Non-@Prelude@ reexports
              -- ** 'Data.Foldable'
              , module Data.Foldable
              -- ** 'Text.Read'
              , module Text.Read
              -- * Original @Prelude@ reexports
              , module Prelude
              ) where

import           Data.Foldable      (foldl')
import           Data.List.NonEmpty (NonEmpty (..))
import           Text.Read          (readMaybe)

import           Prelude            hiding (foldl1, foldr1, head, init, last,
                                     maximum, minimum, product, sum, tail)
import qualified Prelude

-- | A variant of 'foldr' that has no base case, that when applied to an empty
-- structure, returns 'Nothing'.
foldr1 :: Foldable t => (a -> a -> a) -> t a -> Maybe a
foldr1 :: (a -> a -> a) -> t a -> Maybe a
foldr1 a -> a -> a
f = (a -> Maybe a -> Maybe a) -> Maybe a -> t a -> Maybe a
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr a -> Maybe a -> Maybe a
mf Maybe a
forall a. Maybe a
Nothing
    where
        mf :: a -> Maybe a -> Maybe a
mf a
x Maybe a
Nothing  = a -> Maybe a
forall a. a -> Maybe a
Just a
x
        mf a
x (Just a
y) = a -> Maybe a
forall a. a -> Maybe a
Just (a -> a -> a
f a
x a
y)

-- | A variant of 'foldl' that has no base case, that when applied to an empty
-- structure, returns 'Nothing'.
foldl1 :: Foldable t => (a -> a -> a) -> t a -> Maybe a
foldl1 :: (a -> a -> a) -> t a -> Maybe a
foldl1 a -> a -> a
f = (Maybe a -> a -> Maybe a) -> Maybe a -> t a -> Maybe a
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Maybe a -> a -> Maybe a
mf Maybe a
forall a. Maybe a
Nothing
    where
        mf :: Maybe a -> a -> Maybe a
mf Maybe a
Nothing  a
x = a -> Maybe a
forall a. a -> Maybe a
Just a
x
        mf (Just a
y) a
x = a -> Maybe a
forall a. a -> Maybe a
Just (a -> a -> a
f a
y a
x)

-- | 'Prelude.foldr1' applied to a 'NonEmpty' list, guaranteeing no error is
-- thrown.
foldr1' :: (a -> a -> a) -> NonEmpty a -> a
foldr1' :: (a -> a -> a) -> NonEmpty a -> a
foldr1' = (a -> a -> a) -> NonEmpty a -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
Prelude.foldr1

-- | 'Prelude.foldl1' applied to a 'NonEmpty' list, guaranteeing no error is
-- thrown.
foldl1' :: (a -> a -> a) -> NonEmpty a -> a
foldl1' :: (a -> a -> a) -> NonEmpty a -> a
foldl1' = (a -> a -> a) -> NonEmpty a -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
Prelude.foldl1

-- | The largest element of a structure. Returns 'Nothing' when applied to an
-- empty structure.
maximum :: (Foldable t, Ord a) => t a -> Maybe a
maximum :: t a -> Maybe a
maximum = (a -> a -> a) -> t a -> Maybe a
forall (t :: * -> *) a.
Foldable t =>
(a -> a -> a) -> t a -> Maybe a
foldl1 a -> a -> a
forall a. Ord a => a -> a -> a
max

-- | The smallest element of a structure. Returns 'Nothing' when applied to an
-- empty structure.
minimum :: (Foldable t, Ord a) => t a -> Maybe a
minimum :: t a -> Maybe a
minimum = (a -> a -> a) -> t a -> Maybe a
forall (t :: * -> *) a.
Foldable t =>
(a -> a -> a) -> t a -> Maybe a
foldl1 a -> a -> a
forall a. Ord a => a -> a -> a
min

-- | 'Prelude.maximum' applied to a 'NonEmpty' list, guaranteeing no error is
-- thrown.
maximum' :: (Ord a) => NonEmpty a -> a
maximum' :: NonEmpty a -> a
maximum' = NonEmpty a -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
Prelude.maximum

-- | 'Prelude.minimum' applied to a 'NonEmpty' list, guaranteeing no error is
-- thrown.
minimum' :: (Ord a) => NonEmpty a -> a
minimum' :: NonEmpty a -> a
minimum' = NonEmpty a -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
Prelude.minimum

-- | 'Prelude.sum' but with strict 'foldl'' for better performance. Computes the
-- sum of numbers in a structure.
sum :: (Foldable t, Num a) => t a -> a
sum :: t a -> a
sum = (a -> a -> a) -> a -> t a -> a
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' a -> a -> a
forall a. Num a => a -> a -> a
(+) a
0

-- | 'Prelude.product' but with strict 'foldl'' for better performance. Computes
-- the product of numbers in a structure.
product :: (Foldable t, Num a) => t a -> a
product :: t a -> a
product = (a -> a -> a) -> a -> t a -> a
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' a -> a -> a
forall a. Num a => a -> a -> a
(*) a
1

-- | Extract the first element of a list, returning 'Nothing' when the list is
-- empty.
head :: [a] -> Maybe a
head :: [a] -> Maybe a
head (a
x:[a]
_) = a -> Maybe a
forall a. a -> Maybe a
Just a
x
head []    = Maybe a
forall a. Maybe a
Nothing

-- | Extract the last element of a list, which must be finite, returning
-- 'Nothing' when the list is empty.
last :: [a] -> Maybe a
last :: [a] -> Maybe a
last = (Maybe a -> a -> Maybe a) -> Maybe a -> [a] -> Maybe a
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (\Maybe a
_ a
x -> a -> Maybe a
forall a. a -> Maybe a
Just a
x) Maybe a
forall a. Maybe a
Nothing

-- | Extract the elements after the head of a list, returning 'Nothing' when the
-- list is empty.
tail :: [a] -> Maybe [a]
tail :: [a] -> Maybe [a]
tail (a
_:[a]
xs) = [a] -> Maybe [a]
forall a. a -> Maybe a
Just [a]
xs
tail []     = Maybe [a]
forall a. Maybe a
Nothing

-- | Return all the elements of a list except the last one, returning 'Nothing'
-- when the list is empty.
init :: [a] -> Maybe [a]
init :: [a] -> Maybe [a]
init []     = Maybe [a]
forall a. Maybe a
Nothing
init [a
_]    = [a] -> Maybe [a]
forall a. a -> Maybe a
Just []
init (a
x:[a]
xs) = (a
xa -> [a] -> [a]
forall a. a -> [a] -> [a]
:) ([a] -> [a]) -> Maybe [a] -> Maybe [a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [a] -> Maybe [a]
forall a. [a] -> Maybe [a]
init [a]
xs

-- | List safe index (subscript) operator, starting from 0, returning 'Nothing'
-- when out of bounds.
(!?) :: [a] -> Int -> Maybe a
(a
x:[a]
_)  !? :: [a] -> Int -> Maybe a
!? Int
0 = a -> Maybe a
forall a. a -> Maybe a
Just a
x
(a
_:[a]
xs) !? Int
n = if Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 then Maybe a
forall a. Maybe a
Nothing else [a]
xs [a] -> Int -> Maybe a
forall a. [a] -> Int -> Maybe a
!? (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
[]     !? Int
_ = Maybe a
forall a. Maybe a
Nothing