module Contravariant.Extras
(
  {-|
  @contrazip@ functions of multiple arities.
  -}
  module Contravariant.Extras.Contrazip,
  {-|
  @contrazipLifting@ functions of multiple arities.
  -}
  module Contravariant.Extras.ContrazipLifting,
  (>*<),
  contramany,
  Supplied(..),
)
where

import Contravariant.Extras.Prelude hiding ((<>))
import Contravariant.Extras.Contrazip
import Contravariant.Extras.ContrazipLifting
import Data.Functor.Contravariant.Divisible
import Data.Semigroup (Semigroup ((<>)))


-- |
-- An alias to 'divided'.
{-# INLINE (>*<) #-}
(>*<) :: Divisible f => f a -> f b -> f (a, b)
(>*<) =
  divided

contramany :: Decidable f => f a -> f [a]
contramany f =
  loop
  where
    loop =
      choose chooser cons nil
      where
        chooser =
          \case
            head : tail ->
              Left (head, tail)
            _ ->
              Right ()
        cons =
          divide id f loop
        nil =
          conquer

-- |
-- A combination of a divisible functor with some input for it.
-- Allows to use the 'Monoid' API for composition.
data Supplied divisible =
  forall input. Supplied (divisible input) input

instance Divisible divisible => Semigroup (Supplied divisible) where
  Supplied divisible1 input1 <> Supplied divisible2 input2 =
    Supplied divisible3 input3
    where
      divisible3 =
        divide id divisible1 divisible2
      input3 =
        (input1, input2)

instance Divisible divisible => Monoid (Supplied divisible) where
  mempty =
    Supplied conquer ()
  mappend =
    (<>)