{-# LANGUAGE Safe #-}

{- |
Copyright:  (c) 2018-2020 Kowainik
SPDX-License-Identifier: MIT
Maintainer:  Kowainik <xrom.xkov@gmail.com>
Stability:   Experimental
Portability: Portable

Contains utility functions for working with tuples.

@since 0.6.0.0
-}

module Relude.Extra.Foldable
    ( foldlSC
    ) where

import Relude


{- | A left-associative fold that's tail-recursive but can still short-circuit.
Returning a 'Left' short-circuits and immediately returns the value inside.
Returning a 'Right' continues the fold as usual with the value inside.

>>> foldlSC (\acc x -> if x == 0 then Left 0 else Right $! acc * x) 1 [1..6]
720
>>> foldlSC (\acc x -> if x == 0 then Left 0 else Right $! acc * x) 1 (0:error "Short-circuiting should keep this from happening")
0

@since 0.6.0.0
-}
foldlSC :: forall t b a. Foldable t => (b -> a -> Either b b) -> b -> t a -> b
foldlSC :: (b -> a -> Either b b) -> b -> t a -> b
foldlSC f :: b -> a -> Either b b
f = (t a -> b -> b) -> b -> t a -> b
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((t a -> b -> b) -> b -> t a -> b)
-> (t a -> b -> b) -> b -> t a -> b
forall a b. (a -> b) -> a -> b
$ (a -> (b -> b) -> b -> b) -> (b -> b) -> t a -> b -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr a -> (b -> b) -> b -> b
go b -> b
forall a. a -> a
id
  where
    go :: a -> (b -> b) -> b -> b
    go :: a -> (b -> b) -> b -> b
go x :: a
x k :: b -> b
k z :: b
z = case b -> a -> Either b b
f b
z a
x of
        Left l :: b
l  -> b
l
        Right r :: b
r -> b -> b
k b
r
{-# INLINE foldlSC #-}