packages feed

predicate-typed-0.7.4.4: src/Predicate/Data/Foldable.hs

{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE NoStarIsType #-}
{-# LANGUAGE EmptyDataDeriving #-}
{-# LANGUAGE ViewPatterns #-}
-- | promoted foldable functions
module Predicate.Data.Foldable (
    Concat
  , ConcatMap
  , Cycle
  , FoldAla
  , FoldMap

  , ToListExt
  , FromList
  , FromListExt

  , ToList

  , IToList
  , IToList'

  , ToNEList

  , OneP

  , Null
  , Null'
  , IsEmpty

  , Ands
  , Ors
 ) where
import Predicate.Core
import Predicate.Misc
import Predicate.Util
import Predicate.Data.Monoid (MConcat)
import Control.Lens
import Data.Typeable (Typeable, Proxy(Proxy))
import Data.Kind (Type)
import Data.Foldable (Foldable(toList,fold))
import qualified Data.List.NonEmpty as N
import Data.List.NonEmpty (NonEmpty(..))
import qualified GHC.Exts as GE
import Data.List (findIndex)
-- $setup
-- >>> import Predicate.Prelude
-- >>> :set -XDataKinds
-- >>> :set -XTypeApplications
-- >>> :set -XTypeOperators
-- >>> :set -XAllowAmbiguousTypes
-- >>> :set -XOverloadedStrings
-- >>> :set -XNoOverloadedLists
-- >>> :set -XFlexibleContexts
-- >>> import qualified Data.Map.Strict as M
-- >>> import qualified Data.Set as Set
-- >>> import qualified Data.Text as T
-- >>> import qualified Data.Semigroup as SG
-- >>> import Data.These
-- >>> import Data.Time

-- | create a 'NonEmpty' list from a 'Foldable'
--
-- >>> pz @ToNEList []
-- Fail "empty list"
--
-- >>> pz @ToNEList [1,2,3,4,5]
-- Val (1 :| [2,3,4,5])
--
data ToNEList deriving Show
instance ( Show (t a)
         , Foldable t
         ) => P ToNEList (t a) where
  type PP ToNEList (t a) = NonEmpty a
  eval _ opts as =
    let msg0 = "ToNEList"
    in pure $ case toList as of
         [] -> mkNode opts (Fail "empty list") msg0 []
         x:xs -> mkNode opts (Val (x N.:| xs)) (msg0 <> showVerbose opts " " as) []


-- cant directly create a singleton type using '[] since the type of '[] is unknown. instead use 'Singleton' or 'EmptyT'

-- | similar to 'null' using 'AsEmpty'
--
-- >>> pz @IsEmpty [1,2,3,4]
-- Val False
--
-- >>> pz @IsEmpty []
-- Val True
--
-- >>> pz @IsEmpty LT
-- Val False
--
-- >>> pz @IsEmpty EQ
-- Val True
--
-- >>> pl @IsEmpty ("failed11" :: T.Text)
-- False (IsEmpty | "failed11")
-- Val False
--
-- >>> pl @IsEmpty ("" :: T.Text)
-- True (IsEmpty | "")
-- Val True
--
data IsEmpty deriving Show

instance ( Show as
         , AsEmpty as
         ) => P IsEmpty as where
  type PP IsEmpty as = Bool
  eval _ opts as =
    let b = has _Empty as
    in pure $ mkNodeB opts b ("IsEmpty" <> showVerbose opts " | " as) []

-- | explicit version of 'IToList' with an extra parameter @p@ to point to the value
--
-- >>> pz @(IToList' (Hole _) Snd) (True,"aBc" :: String)
-- Val [(0,'a'),(1,'B'),(2,'c')]
--
data IToList' t p deriving Show

instance ( Typeable (PP t x)
         , Show (PP t x)
         , FoldableWithIndex (PP t x) f
         , PP p x ~ f a
         , P p x
         , Show x
         , Show a
         ) => P (IToList' t p) x where
  type PP (IToList' t p) x = [(PP t x, ExtractAFromTA (PP p x))]
  eval _ opts x = do
    let msg0 = "IToList"
        t = showT @(PP t x)
    pp <- eval (Proxy @p) opts x
    pure $ case getValueLR NoInline opts msg0 pp [] of
      Left e -> e
      Right p ->
        let b = itoList p
        in mkNode opts (Val b) (msg0 <> "(" <> t <> ") " <> showL opts b <> showVerbose opts " | " x) [hh pp]

-- | similar to 'Control.Lens.itoList'
--
-- >>> pz @(IToList _) ("aBc" :: String)
-- Val [(0,'a'),(1,'B'),(2,'c')]
--
-- >>> pl @(IToList _) ("abcd" :: String)
-- Present [(0,'a'),(1,'b'),(2,'c'),(3,'d')] (IToList(Int) [(0,'a'),(1,'b'),(2,'c'),(3,'d')] | "abcd")
-- Val [(0,'a'),(1,'b'),(2,'c'),(3,'d')]
--
-- >>> pl @(IToList _) (M.fromList $ itoList ("abcd" :: String))
-- Present [(0,'a'),(1,'b'),(2,'c'),(3,'d')] (IToList(Int) [(0,'a'),(1,'b'),(2,'c'),(3,'d')] | fromList [(0,'a'),(1,'b'),(2,'c'),(3,'d')])
-- Val [(0,'a'),(1,'b'),(2,'c'),(3,'d')]
--
-- >>> pl @(IToList _) [9,2,7,4]
-- Present [(0,9),(1,2),(2,7),(3,4)] (IToList(Int) [(0,9),(1,2),(2,7),(3,4)] | [9,2,7,4])
-- Val [(0,9),(1,2),(2,7),(3,4)]
--
-- >>> pl @(IToList _) (M.fromList (zip ['a'..] [9,2,7,4]))
-- Present [('a',9),('b',2),('c',7),('d',4)] (IToList(Char) [('a',9),('b',2),('c',7),('d',4)] | fromList [('a',9),('b',2),('c',7),('d',4)])
-- Val [('a',9),('b',2),('c',7),('d',4)]
--
-- >>> pl @(IToList _) (Just 234)
-- Present [((),234)] (IToList(()) [((),234)] | Just 234)
-- Val [((),234)]
--
-- >>> pl @(IToList _) (Nothing @Double)
-- Present [] (IToList(()) [] | Nothing)
-- Val []
--
-- >>> pl @(IToList _) [1..5]
-- Present [(0,1),(1,2),(2,3),(3,4),(4,5)] (IToList(Int) [(0,1),(1,2),(2,3),(3,4),(4,5)] | [1,2,3,4,5])
-- Val [(0,1),(1,2),(2,3),(3,4),(4,5)]
--
-- >>> pl @(IToList _) ['a','b','c']
-- Present [(0,'a'),(1,'b'),(2,'c')] (IToList(Int) [(0,'a'),(1,'b'),(2,'c')] | "abc")
-- Val [(0,'a'),(1,'b'),(2,'c')]
--
data IToList (t :: Type) deriving Show
type IToListT (t :: Type) = IToList' (Hole t) Id

instance P (IToListT t) x => P (IToList t) x where
  type PP (IToList t) x = PP (IToListT t) x
  eval _ = eval (Proxy @(IToListT t))

-- | invokes 'GE.toList'
--
-- >>> pz @ToListExt (M.fromList [(1,'x'),(4,'y')])
-- Val [(1,'x'),(4,'y')]
--
-- >>> pz @ToListExt (T.pack "abc")
-- Val "abc"
--
data ToListExt deriving Show

instance ( Show l
         , GE.IsList l
         , Show (GE.Item l)
         ) => P ToListExt l where
  type PP ToListExt l = [GE.Item l]
  eval _ opts as =
    let msg0 = "ToListExt"
        z = GE.toList as
    in pure $ mkNode opts (Val z) (show3 opts msg0 z as) []

-- | invokes 'GE.fromList'
--
-- >>> run @('OMsg "Fred" ':# 'OLite ':# 'OColorOff) @(FromList (Set.Set Int) << '[2,1,5,5,2,5,2]) ()
-- Fred >>> Present fromList [1,2,5] ((>>) fromList [1,2,5] | {FromList fromList [1,2,5]})
-- Val (fromList [1,2,5])
--
-- >>> pl @(FromList (M.Map _ _) >> Id !! C "y") [('x',True),('y',False)]
-- Present False ((>>) False | {IxL('y') False | p=fromList [('x',True),('y',False)] | q='y'})
-- Val False
--
-- >>> pl @(FromList (M.Map _ _) >> Id !! C "z") [('x',True),('y',False)]
-- Error (!!) index not found (IxL('z') | fromList [('x',True),('y',False)])
-- Fail "(!!) index not found"
--
-- >>> pl @(FromList (M.Map _ _)) [(4,"x"),(5,"dd")]
-- Present fromList [(4,"x"),(5,"dd")] (FromList fromList [(4,"x"),(5,"dd")])
-- Val (fromList [(4,"x"),(5,"dd")])
--
data FromList (t :: Type) deriving Show
-- doesnt work with OverloadedLists unless you cast to [a] explicitly

instance ( a ~ GE.Item t
         , Show t
         , GE.IsList t
         , [a] ~ x
         ) => P (FromList t) x where
  type PP (FromList t) x = t
  eval _ opts as =
    let msg0 = "FromList"
        z = GE.fromList (as :: [GE.Item t]) :: t
    in pure $ mkNode opts (Val z) (msg0 <> " " <> showL opts z) []

-- | invokes 'GE.fromList'
--
-- requires the OverloadedLists extension
--
-- >>> :set -XOverloadedLists
-- >>> pz @(FromListExt (M.Map _ _)) [(4,"x"),(5,"dd")]
-- Val (fromList [(4,"x"),(5,"dd")])
--
data FromListExt (t :: Type) deriving Show
-- l ~ l' is key
instance ( Show l
         , GE.IsList l
         , l ~ l'
         ) => P (FromListExt l') l where
  type PP (FromListExt l') l = l'
  eval _ opts as =
    let msg0 = "FromListExt"
        z = GE.fromList (GE.toList @l as)
    in pure $ mkNode opts (Val z) (msg0 <> " " <> showL opts z) []

-- | similar to 'concat'
--
-- >>> pz @Concat (Just "abc")
-- Val "abc"
--
-- >>> pz @Concat (Left 123)
-- Val []
--
-- >>> pz @Concat ["abc","D","eF","","G"]
-- Val "abcDeFG"
--
-- >>> pz @(Snd >> Concat) ('x',["abc","D","eF","","G"])
-- Val "abcDeFG"
--
data Concat deriving Show

instance ( Show x
         , x ~ t [a]
         , Show a
         , Foldable t
         ) => P Concat x where
  type PP Concat x = ExtractAFromTA x
  eval _ opts x =
    let msg0 = "Concat"
        b = concat x
    in pure $ mkNode opts (Val b) (show3 opts msg0 b x) []

-- | similar to 'concatMap'
data ConcatMap p q deriving Show
type ConcatMapT p q = Map' p q >> Concat

instance P (ConcatMapT p q) x => P (ConcatMap p q) x where
  type PP (ConcatMap p q) x = PP (ConcatMapT p q) x
  eval _ = eval (Proxy @(ConcatMapT p q))


-- | similar to 'cycle' but for a fixed number @n@: for an empty list it just returns an empty list
--
-- >>> pz @(Cycle 5 Id) [1,2]
-- Val [1,2,1,2,1]
--
-- >>> pz @(Cycle 5 Id) []
-- Val []
--
data Cycle n p deriving Show

instance ( PP p x ~ t a
         , Show x
         , P p x
         , Integral (PP n x)
         , P n x
         , Foldable t
         ) => P (Cycle n p) x where
  type PP (Cycle n p) x = [ExtractAFromTA (PP p x)]
  eval _ opts x = do
    let msg0 = "Cycle"
    lr <- runPQ NoInline msg0 (Proxy @n) (Proxy @p) opts x []
    pure $ case lr of
      Left e -> e
      Right (fromIntegral -> n,p,nn,pp) ->
        let hhs = [hh nn, hh pp]
        in case chkSize opts msg0 p hhs of
            Left e ->  e
            Right (_,ps) ->
              let msg1 = msg0 <> "(" <> show n <> ")"
                  d = take n (cycle' ps)
              in mkNode opts (Val d) (showVerbose opts msg1 x) hhs

-- | similar to 'toList'
--
-- >>> pz @ToList "aBc"
-- Val "aBc"
--
-- >>> pz @ToList (Just 14)
-- Val [14]
--
-- >>> pz @ToList Nothing
-- Val []
--
-- >>> pz @ToList (Left "xx")
-- Val []
--
-- >>> pz @ToList (These 12 "xx")
-- Val ["xx"]
--
-- >>> pl @ToList (M.fromList $ zip [0..] "abcd")
-- Present "abcd" (ToList fromList [(0,'a'),(1,'b'),(2,'c'),(3,'d')])
-- Val "abcd"
--
-- >>> pl @ToList (Just 123)
-- Present [123] (ToList Just 123)
-- Val [123]
--
-- >>> pl @ToList (M.fromList (zip ['a'..] [9,2,7,4]))
-- Present [9,2,7,4] (ToList fromList [('a',9),('b',2),('c',7),('d',4)])
-- Val [9,2,7,4]
--
data ToList deriving Show

instance ( Show (t a)
         , Foldable t
         ) => P ToList (t a) where
  type PP ToList (t a) = [a]
  eval _ opts as =
    let msg0 = "ToList"
        z = toList as
    in pure $ mkNode opts (Val z) (msg0 <> showVerbose opts " " as) []

-- | explicit version of 'Null' with an extra parameter @p@ to point to the value
data Null' p deriving Show

instance ( Show (t a)
         , Foldable t
         , t a ~ PP p x
         , P p x
         ) => P (Null' p) x where
  type PP (Null' p) x = Bool
  eval _ opts x = do
    let msg0 = "Null"
    pp <- eval (Proxy @p) opts x
    pure $ case getValueLR NoInline opts msg0 pp [] of
      Left e -> e
      Right p ->
        let b = null p
        in mkNodeB opts b ("Null" <> showVerbose opts " | " p) [hh pp]

-- | similar to 'null' using 'Foldable'
--
-- >>> pz @Null [1,2,3,4]
-- Val False
--
-- >>> pz @Null []
-- Val True
--
-- >>> pz @Null Nothing
-- Val True
--
data Null deriving Show
type NullT = Null' Id

instance P NullT a => P Null a where
  type PP Null a = Bool
  eval _ = evalBool (Proxy @NullT)

-- | similar to 'Data.Foldable.foldMap'
--
-- >>> pl @(FoldMap (Wrap (SG.Sum _) Id)) (Left "x")
-- Present Sum {getSum = 0} (FoldMap <skipped>)
-- Val (Sum {getSum = 0})
--
-- >>> pz @(FoldMap (Wrap (SG.Sum _) Id)) [1..5]
-- Val (Sum {getSum = 15})
--
-- >>> pl @(FoldMap (Wrap (SG.Sum _) Id)) (Right 123)
-- Present Sum {getSum = 123} (FoldMap Wrap Sum {getSum = 123} | 123)
-- Val (Sum {getSum = 123})
--
-- >>> pl @(FoldMap (Map Len)) (Just ["abc","defg","h"])
-- Present [3,4,1] (FoldMap Map [3,4,1] | ["abc","defg","h"])
-- Val [3,4,1]
--
-- >>> pz @(FoldMap (Map Len)) (Just ["abc","defg","h"])
-- Val [3,4,1]
--
-- >>> pz @(FoldMap (Wrap (SG.Sum _) Len)) ["abc","defg","h"]
-- Val (Sum {getSum = 8})
--
-- >>> pz @(FoldMap (FoldMap (Wrap (SG.Sum _) Id))) (Just [1..10])
-- Val (Sum {getSum = 55})
--
data FoldMap p deriving Show

instance ( Traversable n
         , Monoid t
         , PP p a ~ t
         , P p a
         ) => P (FoldMap p) (n a) where
  type PP (FoldMap p) (n a) = PP p a
  eval _ opts na = do
    let msg0 = "FoldMap"
    nttb <- traverse
              (fmap (\tt -> tt & ttString %~ litL opts
                               & ttForest .~ [hh tt]) . eval (Proxy @p) opts)
              na
    let ttnb = sequenceA nttb
    pure $ case getValueLR Inline opts "" ttnb [] of
      Left e -> e
      Right ret ->
        let ind = case foldMap pure ret of
                    [] -> " <skipped>"
                    _:_ -> ""
            d = fold ret
        in ttnb & ttVal .~ Val d
                & ttString %~ (msg0 <>) . (ind <>) . nullIf " "

-- | wraps each item in the foldable container and then unwraps the mconcatenated result: uses 'Control.Lens.Wrapped.Wrapped'
--
-- >>> pz @(FoldAla (SG.Sum _)) [44, 12, 3]
-- Val 59
--
-- >>> pz @(FoldAla (SG.Product _)) [44, 12, 3]
-- Val 1584
--
-- >>> type Ands' = FoldAla SG.All
-- >>> pz @Ands' [True,False,True,True]
-- Val False
--
-- >>> pz @Ands' [True,True,True]
-- Val True
--
-- >>> pz @Ands' []
-- Val True
--
-- >>> type Ors' = FoldAla SG.Any
-- >>> pz @Ors' [False,False,False]
-- Val False
--
-- >>> pz @Ors' []
-- Val False
--
-- >>> pz @Ors' [False,False,False,True]
-- Val True
--
-- >>> type AllPositive' = Map Positive >> FoldAla SG.All
-- >>> pz @AllPositive' [3,1,-5,10,2,3]
-- Val False
--
-- >>> type AllNegative' = Map Negative >> FoldAla SG.All
-- >>> pz @AllNegative' [-1,-5,-10,-2,-3]
-- Val True
--
-- >>> :set -XKindSignatures
-- >>> type Max' (t :: Type) = FoldAla (SG.Max t) -- requires t be Bounded for monoid instance
-- >>> pz @(Max' Int) [10,4,5,12,3,4]
-- Val 12
--
-- >>> pl @(FoldAla (SG.Sum _)) [14,8,17,13]
-- Present 52 ((>>) 52 | {getSum = 52})
-- Val 52
--
-- >>> pl @(FoldAla (SG.Max _)) [14 :: Int,8,17,13] -- allowed as the values are Bounded!
-- Present 17 ((>>) 17 | {getMax = 17})
-- Val 17
--
-- >>> pl @((Len >> (Elem Id '[4,7,1] || (Mod Id 3 >> Same 0))) || (FoldAla (SG.Sum _) >> Gt 200)) [1..20]
-- True (False || True)
-- Val True
--
-- >>> pl @((Len >> (Elem Id '[4,7,1] || (Mod Id 3 >> Same 0))) || (FoldAla (SG.Sum _) >> Gt 200)) [1..19]
-- False (False || False | ((>>) False | {1 == 0}) || ((>>) False | {190 > 200}))
-- Val False
--
-- >>> pl @((Len >> (Elem Id '[4,7,1] || (Mod Id 3 >> Same 0))) || (FoldAla (SG.Sum _) >> Gt 200)) []
-- True (True || False)
-- Val True
--
-- >>> pl @((Len >> (Elem Id '[4,7,1] || (Mod Id 3 >> Same 0))) &&& FoldAla (SG.Sum _)) [1..20]
-- Present (False,210) ('(False,210))
-- Val (False,210)
--
-- >>> pl @(FoldAla SG.Any) [False,False,True,False]
-- Present True ((>>) True | {getAny = True})
-- Val True
--
-- >>> pl @(FoldAla SG.All) [False,False,True,False]
-- Present False ((>>) False | {getAll = False})
-- Val False
--
-- >>> pl @(FoldAla (SG.Sum _)) (Just 13)
-- Present 13 ((>>) 13 | {getSum = 13})
-- Val 13
--
-- >>> pl @(FoldAla (SG.Sum _)) [1..10]
-- Present 55 ((>>) 55 | {getSum = 55})
-- Val 55
--
data FoldAla (t :: Type) deriving Show
type FoldAlaT (t :: Type) = Map' (Wrap t Id) Id >> MConcat >> Unwrap

instance P (FoldAlaT t) x => P (FoldAla t) x where
  type PP (FoldAla t) x = PP (FoldAlaT t) x
  eval _ = eval (Proxy @(FoldAlaT t))

-- | similar to 'Data.Foldable.and'
--
-- >>> pz @Ands [True,True,True]
-- Val True
--
-- >>> pl @Ands [True,True,True,False]
-- False (Ands(4) i=3 | [True,True,True,False])
-- Val False
--
-- >>> pz @Ands []
-- Val True
--
data Ands deriving Show

instance ( x ~ t a
         , Foldable t
         , a ~ Bool
         ) => P Ands x where
  type PP Ands x = Bool
  eval _ opts x' =
    let msg0 = "Ands"
    in pure $ case chkSize opts msg0 x' [] of
         Left e -> e
         Right (xLen,x) ->
           let msg1 = msg0 ++ "(" ++ show xLen ++ ")"
               w = case findIndex not x of
                     Nothing -> ""
                     Just i -> " i="++show i
           in mkNodeB opts (and x) (msg1 <> w <> showVerbose opts " | " x) []

-- | similar to 'Data.Foldable.or'
--
-- >>> pz @Ors [False,False,False]
-- Val False
--
-- >>> pl @Ors [True,True,True,False]
-- True (Ors(4) i=0 | [True,True,True,False])
-- Val True
--
-- >>> pl @Ors []
-- False (Ors(0) | [])
-- Val False
--
data Ors deriving Show

instance ( x ~ t a
         , Foldable t
         , a ~ Bool
         ) => P Ors x where
  type PP Ors x = Bool
  eval _ opts x' =
    let msg0 = "Ors"
    in pure $ case chkSize opts msg0 x' [] of
         Left e -> e
         Right (xLen,x) ->
           let msg1 = msg0 ++ "(" ++ show xLen ++ ")"
               w = case findIndex id x of
                     Nothing -> ""
                     Just i -> " i="++show i
           in mkNodeB opts (or x) (msg1 <> w <> showVerbose opts " | " x) []

-- | gets the singleton value from a foldable
--
-- >>> pl @OneP [10..15]
-- Error OneP:expected one element(6)
-- Fail "OneP:expected one element(6)"
--
-- >>> pl @OneP [10]
-- Present 10 (OneP)
-- Val 10
--
-- >>> pl @OneP []
-- Error OneP:expected one element(empty)
-- Fail "OneP:expected one element(empty)"
--
-- >>> pl @OneP (Just 10)
-- Present 10 (OneP)
-- Val 10
--
-- >>> pl @OneP Nothing
-- Error OneP:expected one element(empty)
-- Fail "OneP:expected one element(empty)"
--
data OneP deriving Show

instance ( Foldable t
         , x ~ t a
         ) => P OneP x where
  type PP OneP x = ExtractAFromTA x
  eval _ opts x = do
    let msg0 = "OneP"
    pure $ case toList x of
      [] -> mkNode opts (Fail (msg0 <> ":expected one element(empty)")) "" []
      [a] -> mkNode opts (Val a) msg0 []
      as' -> case chkSize opts msg0 as' [] of
               Left e -> e
               Right (asLen,_) ->
                 mkNode opts (Fail (msg0 <> ":expected one element(" <> show asLen <> ")")) "" []

--type OneP = Guard "expected list of length 1" (Len == 1) >> Head
--type OneP = Guard (PrintF "expected list of length 1 but found length=%d" Len) (Len == 1) >> Head