-- | -- Module : Data.InsertLeft -- Copyright : (c) Oleksandr Zhabenko 2020-2024 -- License : MIT -- Stability : Experimental -- Maintainer : oleksandr.zhabenko@yahoo.com -- -- Some extension to the 'F.Foldable' and 'Monoid' classes. Introduces a new class 'InsertLeft' -- the class of types of values that can be inserted from the left -- to the 'F.Foldable' structure that is simultaneously the data that is also the 'Monoid' -- instance. For lists as instances of 'InsertLeft' and 'Monoid' just use the basic library -- functions from GHC.List or Data.List modules where possible. -- Is a fork of . {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, NoImplicitPrelude #-} module Data.InsertLeft ( InsertLeft(..) , subG , takeFromEndG , reverseTakeFromEndG , dropFromEndG , reverseDropFromEndG , takeWhile , dropWhile , span , splitAtEndG , preAppend , safeHeadG , safeInitG , safeLastG , mapG , filterG , partitionG -- * Not recommended for performance reasons, provided if there is no other acceptable possibilities (as fallback placeholders) , reverseTakeG , takeG , reverseDropG , dropG , splitAtG , safeTailG ) where import GHC.Base import GHC.Num import GHC.Real import Data.Tuple import qualified Data.Foldable as F import Data.Monoid infixr 1 %@, %^ -- | Some extension to the 'F.Foldable' and 'Monoid' classes. class (F.Foldable t, Eq a, Eq (t a)) => InsertLeft t a where (%@) :: a -> t a -> t a -- infixr 1 (%^) :: t a -> t (t a) -> t (t a) instance (Eq a) => InsertLeft [] a where (%@) = (:) (%^) = (:) -- | Inspired by: https://hackage.haskell.org/package/base-4.14.0.0/docs/src/Data.OldList.html#words -- and: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. Is similar to the 'Prelude.words' but operates on more general -- structures an allows more control. subG :: (InsertLeft t a, Monoid (t a), Monoid (t (t a))) => t a -> t a -> t (t a) subG whspss xs = if F.null ts then mempty else w %^ subG whspss s'' where ts = dropWhile (`F.elem` whspss) xs (w, s'') = span (`F.notElem` whspss) ts {-# SPECIALIZE subG :: String -> String -> [String] #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. dropWhile' :: (InsertLeft t a, Monoid (t a)) => (a -> Bool) -> t a -> (t a, t a) dropWhile' p = F.foldr f v where f x (ys, xs) = (if p x then ys else x %@ xs, x %@ xs) v = (mempty,mempty) -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. dropWhile :: (InsertLeft t a, Monoid (t a)) => (a -> Bool) -> t a -> t a dropWhile p = fst . dropWhile' p -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. span :: (InsertLeft t a, Monoid (t a)) => (a -> Bool) -> t a -> (t a, t a) span p = (\(x, y, _) -> (x, y)) . span' p -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. span' :: (InsertLeft t a, Monoid (t a)) => (a -> Bool) -> t a -> (t a, t a, t a) span' p = F.foldr f v where f x (ys, zs, xs) | p x = (x %@ ys, zs, x %@ xs) | otherwise = (mempty,x %@ xs, x %@ xs) v = (mempty, mempty, mempty) -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. takeWhile :: (InsertLeft t a, Monoid (t a)) => (a -> Bool) -> t a -> t a takeWhile p = fst . takeWhile' p -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. takeWhile' :: (InsertLeft t a, Monoid (t a)) => (a -> Bool) -> t a -> (t a, t a) takeWhile' p = F.foldr f v where f x (ys,xs) = (if p x then x %@ ys else mempty, x %@ xs) v = (mempty,mempty) -- | Prepends and appends the given two first arguments to the third one. preAppend :: (InsertLeft t a, Monoid (t (t a))) => t a -> t (t a) -> t (t a) -> t (t a) preAppend ts uss tss = mconcat [ts %^ tss, uss] {-# INLINE preAppend #-} {-# SPECIALIZE preAppend :: String -> [String] -> [String] -> [String] #-} ------------------------------------------------------------------------------------- -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. -- Takes the first argument quantity from the right end of the structure preserving the order. takeFromEndG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> t a takeFromEndG n = (\(xs,_) -> xs) . F.foldr f v where v = (mempty,0) f x (zs,k) | k < n = (x %@ zs,k + 1) | otherwise = (zs,k) {-# SPECIALIZE takeFromEndG :: Int -> String -> String #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. -- Takes the specified quantity from the right end of the structure and then reverses the result. reverseTakeFromEndG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> t a reverseTakeFromEndG n = (\(xs,_) -> xs) . F.foldr f v where v = (mempty,0) f x (zs,k) | k < n = (zs `mappend` (x %@ mempty),k + 1) | otherwise = (zs,k) {-# SPECIALIZE reverseTakeFromEndG :: Int -> String -> String #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. -- Is analogous to the taking the specified quantity from the structure and then reversing the result. Uses strict variant of the foldl, so is -- not suitable for large amounts of data. Not recommended for performance reasons. For lists just -- use the combination @(reverse . take n)@. reverseTakeG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> t a reverseTakeG n = (\(xs,_) -> xs) . F.foldl' f v where v = (mempty,0) f (zs,k) x | k < n = (x %@ zs,k + 1) | otherwise = (zs,k) {-# SPECIALIZE reverseTakeG :: Int -> String -> String #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. Uses strict variant of the foldl, so is -- strict and the data must be finite. Not recommended for performance reasons. For lists just use -- GHC.List.take n. takeG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> t a takeG n = (\(xs,_) -> xs) . F.foldl' f v where v = (mempty,0) f (zs,k) x | k < n = (zs `mappend` (x %@ mempty),k + 1) | otherwise = (zs,k) {-# SPECIALIZE takeG :: Int -> String -> String #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. -- Is analogous to the dropping the specified quantity from the structure and then reversing the result. Uses strict variant of the foldl, so is -- strict and the data must be finite. Not recommended for performance reasons. For lists just -- use @ (reverse . drop n) combination. reverseDropG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> t a reverseDropG n = (\(xs,_) -> xs) . F.foldl' f v where v = (mempty,0) f (zs,k) x | k < n = (mempty,k + 1) | otherwise = (x %@ zs,k) {-# SPECIALIZE reverseDropG :: Int -> String -> String #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. -- Drops the first argument quantity from the right end of the structure and returns the result preserving the order. dropFromEndG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> t a dropFromEndG n = (\(xs,_) -> xs) . F.foldr f v where v = (mempty,0) f x (zs,k) | k < n = (mempty,k + 1) | otherwise = (x %@ zs,k) {-# SPECIALIZE dropFromEndG :: Int -> String -> String #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. -- Drops the specified quantity from the right end of the structure and then reverses the result. reverseDropFromEndG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> t a reverseDropFromEndG n = (\(xs,_) -> xs) . F.foldr f v where v = (mempty,0) f x (zs,k) | k < n = (mempty,k + 1) | otherwise = (zs `mappend` (x %@ mempty),k) {-# SPECIALIZE reverseDropFromEndG :: Int -> String -> String #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. Uses strict variant of the foldl, so is -- strict and the data must be finite. Not recommended for performance reasons. For lists just use -- the GHC.List.drop. dropG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> t a dropG n = (\(xs,_) -> xs) . F.foldl' f v where v = (mempty,0) f (zs,k) x | k < n = (mempty,k + 1) | otherwise = (zs `mappend` (x %@ mempty),k) {-# SPECIALIZE dropG :: Int -> String -> String #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. Uses strict variant of the foldl, so is -- strict and the data must be finite. Not recommended for performance reasons. For lists just use -- the GHC.List.splitAt. splitAtG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> (t a, t a) splitAtG n = (\(x,y,_) -> (x,y)) . F.foldl' f v where v = (mempty,mempty,0) f (zs,ts,k) x | k < n = (zs `mappend` (x %@ mempty),mempty,k + 1) | otherwise = (zs,ts `mappend` (x %@ mempty),k + 1) {-# SPECIALIZE splitAtG :: Int -> String -> (String,String) #-} {-# SPECIALIZE splitAtG :: (Eq a) => Int -> [a] -> ([a],[a]) #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. Splits the structure starting from the end and preserves the order. splitAtEndG :: (Integral b, InsertLeft t a, Monoid (t a)) => b -> t a -> (t a, t a) splitAtEndG n = (\(x,y,_) -> (y,x)) . F.foldr f v where v = (mempty,mempty,0) f x (zs,ts,k) | k < n = (x %@ zs,mempty,k + 1) | otherwise = (zs,x %@ ts,k + 1) {-# SPECIALIZE splitAtEndG :: Int -> String -> (String,String) #-} {-# SPECIALIZE splitAtEndG :: (Eq a) => Int -> [a] -> ([a],[a]) #-} -- | If a structure is empty, just returns 'Nothing'. safeHeadG :: (F.Foldable t) => t a -> Maybe a safeHeadG = F.find (const True) {-# SPECIALIZE safeHeadG :: [a] -> Maybe a #-} -- | If the structure is empty, just returns itself. Uses strict variant of the foldl, so is -- strict and the data must be finite. Not recommended for performance reasons. For lists just use -- Data.List.tail or something equivalent. safeTailG :: (InsertLeft t a, Monoid (t a)) => t a -> t a safeTailG = dropG 1 {-# SPECIALIZE safeTailG :: (Eq a) => [a] -> [a] #-} -- | If the structure is empty, just returns itself. safeInitG :: (InsertLeft t a, Monoid (t a)) => t a -> t a safeInitG = dropFromEndG 1 {-# SPECIALIZE safeInitG :: (Eq a) => [a] -> [a] #-} -- | If the structure is empty, just returns 'Nothing'. safeLastG :: (InsertLeft t a, Monoid (t a)) => t a -> Maybe a safeLastG = F.find (const True) . takeFromEndG 1 {-# SPECIALIZE safeLastG :: (Eq a) => [a] -> Maybe a #-} ----------------------------------------------------------------------------- -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. Acts similarly to the 'map' function from Prelude. mapG :: (InsertLeft t b, Monoid (t b)) => (a -> b) -> t a -> t b mapG f = F.foldr (\x ys -> f x %@ ys) mempty {-# INLINE mapG #-} {-# SPECIALIZE mapG :: (Eq b) => (a -> b) -> [a] -> [b] #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. Acts similarly to 'filter' function from Prelude. filterG :: (InsertLeft t a, Monoid (t a)) => (a -> Bool) -> t a -> t a filterG p = F.foldr (\x ys -> if p x then x %@ ys else ys) mempty {-# INLINE filterG #-} {-# SPECIALIZE filterG :: (Eq a) => (a -> Bool) -> [a] -> [a] #-} -- | Inspired by: Graham Hutton. A tutorial on the universality and expressiveness of fold. /J. Functional Programming/ 9 (4): 355–372, July 1999. -- that is available at the URL: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf. Acts similarly to 'partition' function from Data.List. Practically is a -- rewritten for more general variants function partition from https://hackage.haskell.org/package/base-4.14.0.0/docs/src/Data.OldList.html#partition partitionG :: (InsertLeft t a, Monoid (t a)) => (a -> Bool) -> t a -> (t a, t a) partitionG p = F.foldr (\x (ys,zs) -> if p x then (x %@ ys,zs) else (ys,x %@ zs)) (mempty,mempty) {-# INLINE partitionG #-} {-# SPECIALIZE partitionG :: (Eq a) => (a -> Bool) -> [a] -> ([a],[a]) #-}