{-
  Copyright 2025 Arista Networks

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-}

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}

-- | Presents right-associative folds as 'Foldable' sequences.
module Proto3.Wire.FoldR
  ( FoldR(..)
  , fromFoldR
  ) where

import Data.Foldable qualified
import Data.Kind (Type)
import GHC.Exts (TYPE)
import GHC.Exts qualified
import Text.Read (Read(..))

-- | Presents a right-associative fold as a 'Foldable' sequence.
--
-- Similar to the @FRList@ example in the documentation for "Data.Foldable",
-- but for generality the element type may be unlifted.  For compatibility
-- with the 'Foldable' type class and to avoid limitations on runtime
-- representation polymorphism, the fold supports only lifted results.
newtype FoldR (a :: TYPE r) = FoldR { forall a. FoldR a -> forall b. (a -> b -> b) -> b -> b
applyFoldR :: forall (b :: Type) . (a -> b -> b) -> b -> b }
  deriving stock ((forall a b. (a -> b) -> FoldR a -> FoldR b)
-> (forall a b. a -> FoldR b -> FoldR a) -> Functor FoldR
forall a b. a -> FoldR b -> FoldR a
forall a b. (a -> b) -> FoldR a -> FoldR b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b. (a -> b) -> FoldR a -> FoldR b
fmap :: forall a b. (a -> b) -> FoldR a -> FoldR b
$c<$ :: forall a b. a -> FoldR b -> FoldR a
<$ :: forall a b. a -> FoldR b -> FoldR a
Functor)

instance Foldable FoldR
  where
    foldr :: forall a b. (a -> b -> b) -> b -> FoldR a -> b
foldr a -> b -> b
f b
z FoldR a
xs = FoldR a -> forall b. (a -> b -> b) -> b -> b
forall a. FoldR a -> forall b. (a -> b -> b) -> b -> b
applyFoldR FoldR a
xs a -> b -> b
f b
z

instance GHC.Exts.IsList (FoldR a)
  where
    type Item (FoldR a) = a
    fromList :: [Item (FoldR a)] -> FoldR a
fromList = [a] -> FoldR a
[Item (FoldR a)] -> FoldR a
forall (t :: * -> *) a. Foldable t => t a -> FoldR a
fromFoldR
    toList :: FoldR a -> [Item (FoldR a)]
toList = FoldR a -> [a]
FoldR a -> [Item (FoldR a)]
forall a. FoldR a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
Data.Foldable.toList

instance Eq a =>
         Eq (FoldR a)
  where
    FoldR a
x == :: FoldR a -> FoldR a -> Bool
== FoldR a
y = FoldR a -> [Item (FoldR a)]
forall l. IsList l => l -> [Item l]
GHC.Exts.toList FoldR a
x [a] -> [a] -> Bool
forall a. Eq a => a -> a -> Bool
== FoldR a -> [Item (FoldR a)]
forall l. IsList l => l -> [Item l]
GHC.Exts.toList FoldR a
y

instance Ord a =>
         Ord (FoldR a)
  where
    FoldR a
x <= :: FoldR a -> FoldR a -> Bool
<= FoldR a
y = FoldR a -> [Item (FoldR a)]
forall l. IsList l => l -> [Item l]
GHC.Exts.toList FoldR a
x [a] -> [a] -> Bool
forall a. Ord a => a -> a -> Bool
<= FoldR a -> [Item (FoldR a)]
forall l. IsList l => l -> [Item l]
GHC.Exts.toList FoldR a
y
    FoldR a
x compare :: FoldR a -> FoldR a -> Ordering
`compare` FoldR a
y = FoldR a -> [Item (FoldR a)]
forall l. IsList l => l -> [Item l]
GHC.Exts.toList FoldR a
x [a] -> [a] -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` FoldR a -> [Item (FoldR a)]
forall l. IsList l => l -> [Item l]
GHC.Exts.toList FoldR a
y

instance Read a =>
         Read (FoldR a)
  where
    readPrec :: ReadPrec (FoldR a)
readPrec = ([a] -> FoldR a) -> ReadPrec [a] -> ReadPrec (FoldR a)
forall a b. (a -> b) -> ReadPrec a -> ReadPrec b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [a] -> FoldR a
[Item (FoldR a)] -> FoldR a
forall l. IsList l => [Item l] -> l
GHC.Exts.fromList ReadPrec [a]
forall a. Read a => ReadPrec a
readPrec

instance Show a =>
         Show (FoldR a)
  where
    showsPrec :: Int -> FoldR a -> ShowS
showsPrec Int
d = Int -> [a] -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
d ([a] -> ShowS) -> (FoldR a -> [a]) -> FoldR a -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FoldR a -> [a]
FoldR a -> [Item (FoldR a)]
forall l. IsList l => l -> [Item l]
GHC.Exts.toList

-- | Creates a 'FoldR' from the 'foldr' of the given 'Foldable' sequence.
fromFoldR :: Foldable t => t a -> FoldR a
fromFoldR :: forall (t :: * -> *) a. Foldable t => t a -> FoldR a
fromFoldR t a
xs = FoldR { applyFoldR :: forall b. (a -> b -> b) -> b -> b
applyFoldR = \a -> b -> b
f b
z -> (a -> b -> b) -> b -> t a -> b
forall a b. (a -> b -> b) -> b -> t a -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr a -> b -> b
f b
z t a
xs }