packages feed

valuations-0.0.1: src/Data/Valuation/ValuationAlgebraOp.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wall -Werror #-}

-- | An operation on a valuation algebra, a function from a set of variables to a value.
module Data.Valuation.ValuationAlgebraOp
  ( ValuationAlgebraOp (..),

    -- * optics
    HasValuationAlgebraOp (..),
    AsValuationAlgebraOp (..),

    -- * combinators
    semigroupValuationAlgebraOp,
    valuationAlgebraOpProjectValuation,
    applyHasValuationAlgebraOp,
    applyAsValuationAlgebraOp,
  )
where

import Control.Arrow (Arrow (..), ArrowApply (..), ArrowChoice (..), ArrowLoop (..))
import Control.Category (Category (..))
import Control.Comonad (Comonad (..), ComonadApply (..))
import Control.Lens
  ( Iso,
    Lens',
    Prism',
    Rewrapped,
    Wrapped (..),
    iso,
    review,
    _Wrapped,
  )
import Control.Monad.Fix (MonadFix (..))
import Control.Monad.Reader.Class (MonadReader (..))
import Control.Monad.Zip (MonadZip (..))
import Control.Selective (Selective (..), selectM)
import Data.Distributive (Distributive (..))
import Data.Either (fromLeft, fromRight)
import Data.Function (fix)
import Data.Functor.Apply (Apply (..))
import Data.Functor.Bind (Bind (..))
import Data.Functor.Extend (Extend (..))
import Data.Functor.Rep (Representable (..))
import Data.Profunctor (Choice (..), Profunctor (..), Strong (..))
import Data.Profunctor.Closed (Closed (..))
import Data.Profunctor.Sieve (Cosieve (..))
import Data.Semigroupoid (Semigroupoid (..))
import Data.Valuation.ProjectValuation (ProjectValuation (..))
import Data.Valuation.Semigroup
  ( Semigroup,
    applySemigroup,
    runSemigroup,
  )
import Prelude hiding (Semigroup, id, (.))
import qualified Prelude

-- $setup
-- >>> :set -Wno-name-shadowing -Wno-type-defaults

-- | A function from a set of variables to a value. Isomorphic to @set var -> v@.
newtype ValuationAlgebraOp set var v
  = ValuationAlgebraOp (set var -> v)

instance (ValuationAlgebraOp set var v ~ t) => Rewrapped (ValuationAlgebraOp set' var' v') t

instance Wrapped (ValuationAlgebraOp set var v) where
  type Unwrapped (ValuationAlgebraOp set var v) = set var -> v
  _Wrapped' =
    iso (\(ValuationAlgebraOp x) -> x) ValuationAlgebraOp

-- | Classy lens for types that contain a 'ValuationAlgebraOp'.
class HasValuationAlgebraOp c set var v | c -> set var v where
  valuationAlgebraOp ::
    Lens' c (ValuationAlgebraOp set var v)

instance HasValuationAlgebraOp (ValuationAlgebraOp set var v) set var v where
  valuationAlgebraOp = id

-- | Classy prism for types that can be constructed from a 'ValuationAlgebraOp'.
class AsValuationAlgebraOp c set var v | c -> set var v where
  _ValuationAlgebraOp ::
    Prism' c (ValuationAlgebraOp set var v)

instance AsValuationAlgebraOp (ValuationAlgebraOp set var v) set var v where
  _ValuationAlgebraOp = id

-- | Iso between a 'ValuationAlgebraOp' producing an endomorphism and a 'ProjectValuation'.
valuationAlgebraOpProjectValuation :: Iso (ValuationAlgebraOp set var (v -> v)) (ValuationAlgebraOp set' var' (v' -> v')) (ProjectValuation v set var) (ProjectValuation v' set' var')
valuationAlgebraOpProjectValuation =
  iso
    (\(ValuationAlgebraOp k) -> ProjectValuation k)
    (\(ProjectValuation k) -> ValuationAlgebraOp k)

-- | Lens to the underlying function of a 'HasValuationAlgebraOp'.
applyHasValuationAlgebraOp :: (HasValuationAlgebraOp op set var v) => Lens' op (set var -> v)
applyHasValuationAlgebraOp = valuationAlgebraOp . _Wrapped

-- | Prism to the underlying function of an 'AsValuationAlgebraOp'.
applyAsValuationAlgebraOp :: (AsValuationAlgebraOp op set var v) => Prism' op (set var -> v)
applyAsValuationAlgebraOp = _ValuationAlgebraOp . _Wrapped

-- |
-- >>> let ValuationAlgebraOp f = fmap (*2) (ValuationAlgebraOp sum :: ValuationAlgebraOp [] Int Int)
-- >>> f [1,2,3]
-- 12
instance Functor (ValuationAlgebraOp set a) where
  fmap f (ValuationAlgebraOp g) = ValuationAlgebraOp (f . g)

-- |
-- >>> import Data.Functor.Apply ((<.>))
-- >>> let ValuationAlgebraOp f = (ValuationAlgebraOp (\s -> (+ sum s)) :: ValuationAlgebraOp [] Int (Int -> Int)) <.> ValuationAlgebraOp product
-- >>> f [1,2,3]
-- 12
instance Apply (ValuationAlgebraOp set a) where
  ValuationAlgebraOp f <.> ValuationAlgebraOp g = ValuationAlgebraOp (\sa -> f sa (g sa))

-- |
-- >>> let ValuationAlgebraOp f = pure 42 :: ValuationAlgebraOp [] Int Int
-- >>> f [1,2,3]
-- 42
--
-- >>> let ValuationAlgebraOp f = (ValuationAlgebraOp (\s -> (+ sum s)) :: ValuationAlgebraOp [] Int (Int -> Int)) <*> ValuationAlgebraOp product
-- >>> f [1,2,3]
-- 12
instance Applicative (ValuationAlgebraOp set a) where
  pure b = ValuationAlgebraOp (const b)
  (<*>) = (<.>)

-- |
-- >>> import Data.Functor.Bind ((>>-))
-- >>> let ValuationAlgebraOp f = (ValuationAlgebraOp sum :: ValuationAlgebraOp [] Int Int) >>- (\n -> ValuationAlgebraOp (\s -> n + product s))
-- >>> f [1,2,3]
-- 12
instance Bind (ValuationAlgebraOp set a) where
  ValuationAlgebraOp f >>- k = ValuationAlgebraOp (\sa -> let ValuationAlgebraOp g = k (f sa) in g sa)

-- |
-- >>> let ValuationAlgebraOp f = (ValuationAlgebraOp sum :: ValuationAlgebraOp [] Int Int) >>= (\n -> ValuationAlgebraOp (\s -> n * length s))
-- >>> f [1,2,3]
-- 18
--
-- >>> let ValuationAlgebraOp f = return 42 :: ValuationAlgebraOp [] Int Int
-- >>> f [1,2,3]
-- 42
instance Monad (ValuationAlgebraOp set a) where
  (>>=) = (>>-)

-- |
-- >>> import Data.Semigroupoid (o)
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = o (ValuationAlgebraOp sum :: ValuationAlgebraOp NonEmpty Int Int) (ValuationAlgebraOp sum)
-- >>> f (1 :| [2, 3])
-- 14
instance (Extend set) => Semigroupoid (ValuationAlgebraOp set) where
  o (ValuationAlgebraOp g) (ValuationAlgebraOp f) = ValuationAlgebraOp (g . extended f)

-- |
-- >>> import Control.Category (id, (.))
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = id :: ValuationAlgebraOp NonEmpty Int Int
-- >>> f (1 :| [2, 3])
-- 1
--
-- >>> import Control.Category ((.))
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = (ValuationAlgebraOp sum :: ValuationAlgebraOp NonEmpty Int Int) . ValuationAlgebraOp sum
-- >>> f (1 :| [2, 3])
-- 14
instance (Comonad set) => Category (ValuationAlgebraOp set) where
  id = ValuationAlgebraOp extract
  ValuationAlgebraOp g . ValuationAlgebraOp f = ValuationAlgebraOp (g . extend f)

-- |
-- >>> import Control.Arrow (arr, first)
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = arr (*2) :: ValuationAlgebraOp NonEmpty Int Int
-- >>> f (3 :| [4, 5])
-- 6
--
-- >>> import Control.Arrow (first)
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = first (ValuationAlgebraOp sum :: ValuationAlgebraOp NonEmpty Int Int)
-- >>> f ((1, "a") :| [(2, "b"), (3, "c")])
-- (6,"a")
instance (Comonad set) => Arrow (ValuationAlgebraOp set) where
  arr f = ValuationAlgebraOp (f . extract)
  first = first'

-- |
-- >>> import Control.Arrow (left)
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = left (ValuationAlgebraOp sum :: ValuationAlgebraOp NonEmpty Int Int)
-- >>> f (Left 1 :| [Left 2, Left 3])
-- Left 6
-- >>> f (Right "hi" :| [Left 2])
-- Right "hi"
instance (Comonad set) => ArrowChoice (ValuationAlgebraOp set) where
  left = left'

-- |
-- >>> import Control.Arrow (app)
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = app :: ValuationAlgebraOp NonEmpty (ValuationAlgebraOp NonEmpty Int Int, Int) Int
-- >>> f ((ValuationAlgebraOp sum, 99) :| [(ValuationAlgebraOp product, 1)])
-- 100
instance (Comonad set) => ArrowApply (ValuationAlgebraOp set) where
  app = ValuationAlgebraOp $ \wpair ->
    let (ValuationAlgebraOp f, _) = extract wpair
     in f (fmap snd wpair)

-- |
-- >>> import Control.Arrow (loop)
-- >>> import Data.Functor.Identity (Identity(..))
-- >>> let ValuationAlgebraOp f = loop (ValuationAlgebraOp (\(Identity (b, _)) -> (b * 2, 0))) :: ValuationAlgebraOp Identity Int Int
-- >>> f (Identity 3)
-- 6
instance (ComonadApply set) => ArrowLoop (ValuationAlgebraOp set) where
  loop (ValuationAlgebraOp f) = ValuationAlgebraOp $ \wa ->
    fst . extract $ fix $ \wbd -> extend f ((,) <$> wa <@> fmap snd wbd)

-- |
-- >>> import Control.Monad.Fix (mfix)
-- >>> let ValuationAlgebraOp f = mfix (\x -> ValuationAlgebraOp (\s -> const 42 x + sum s)) :: ValuationAlgebraOp [] Int Int
-- >>> f [1,2,3]
-- 48
instance MonadFix (ValuationAlgebraOp set var) where
  mfix f = ValuationAlgebraOp (\s -> fix (\a -> let ValuationAlgebraOp g = f a in g s))

-- |
-- >>> import Control.Monad.Zip (mzipWith)
-- >>> let ValuationAlgebraOp f = mzipWith (+) (ValuationAlgebraOp sum) (ValuationAlgebraOp product :: ValuationAlgebraOp [] Int Int)
-- >>> f [1,2,3]
-- 12
instance MonadZip (ValuationAlgebraOp set var) where
  mzipWith f (ValuationAlgebraOp g) (ValuationAlgebraOp h) = ValuationAlgebraOp (\s -> f (g s) (h s))

-- |
-- >>> import Control.Selective (select)
-- >>> let ValuationAlgebraOp f = select (ValuationAlgebraOp (\s -> Left (sum s)) :: ValuationAlgebraOp [] Int (Either Int Int)) (ValuationAlgebraOp (\_ -> (+10)))
-- >>> f [1,2,3]
-- 16
instance Selective (ValuationAlgebraOp set var) where
  select = selectM

-- |
-- >>> import Control.Monad.Reader.Class (ask, local)
-- >>> let ValuationAlgebraOp f = ask :: ValuationAlgebraOp [] Int [Int]
-- >>> f [1,2,3]
-- [1,2,3]
--
-- >>> import Control.Monad.Reader.Class (ask, local)
-- >>> let ValuationAlgebraOp f = local (map (*2)) (ValuationAlgebraOp sum :: ValuationAlgebraOp [] Int Int)
-- >>> f [1,2,3]
-- 12
instance MonadReader (set var) (ValuationAlgebraOp set var) where
  ask = ValuationAlgebraOp id
  local f (ValuationAlgebraOp g) = ValuationAlgebraOp (g . f)

-- |
-- >>> import Data.Profunctor (dimap, lmap, rmap)
-- >>> let ValuationAlgebraOp f = dimap (+1) (*2) (ValuationAlgebraOp sum :: ValuationAlgebraOp [] Int Int)
-- >>> f [1,2,3]
-- 18
--
-- >>> import Data.Profunctor (lmap)
-- >>> let ValuationAlgebraOp f = lmap (*10) (ValuationAlgebraOp sum :: ValuationAlgebraOp [] Int Int)
-- >>> f [1,2,3]
-- 60
--
-- >>> import Data.Profunctor (rmap)
-- >>> let ValuationAlgebraOp f = rmap show (ValuationAlgebraOp sum :: ValuationAlgebraOp [] Int Int)
-- >>> f [1,2,3]
-- "6"
instance (Functor set) => Profunctor (ValuationAlgebraOp set) where
  dimap f g (ValuationAlgebraOp h) = ValuationAlgebraOp (g . h . fmap f)
  lmap f (ValuationAlgebraOp h) = ValuationAlgebraOp (h . fmap f)
  rmap g (ValuationAlgebraOp h) = ValuationAlgebraOp (g . h)

-- |
-- >>> import Data.Profunctor (Strong(..))
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = first' (ValuationAlgebraOp sum :: ValuationAlgebraOp NonEmpty Int Int)
-- >>> f ((1, "a") :| [(2, "b"), (3, "c")])
-- (6,"a")
--
-- >>> import Data.Profunctor (Strong(..))
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = second' (ValuationAlgebraOp sum :: ValuationAlgebraOp NonEmpty Int Int)
-- >>> f (("a", 1) :| [("b", 2), ("c", 3)])
-- ("a",6)
instance (Comonad set) => Strong (ValuationAlgebraOp set) where
  first' (ValuationAlgebraOp f) = ValuationAlgebraOp (\sac -> (f (fmap fst sac), snd (extract sac)))
  second' (ValuationAlgebraOp f) = ValuationAlgebraOp (\sca -> (fst (extract sca), f (fmap snd sca)))

-- |
-- >>> import Data.Profunctor (Choice(..))
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = left' (ValuationAlgebraOp sum :: ValuationAlgebraOp NonEmpty Int Int)
-- >>> f (Left 1 :| [Left 2, Left 3])
-- Left 6
-- >>> f (Right "hi" :| [Left 2])
-- Right "hi"
--
-- >>> import Data.Profunctor (Choice(..))
-- >>> import Data.List.NonEmpty (NonEmpty(..))
-- >>> let ValuationAlgebraOp f = right' (ValuationAlgebraOp sum :: ValuationAlgebraOp NonEmpty Int Int)
-- >>> f (Right 1 :| [Right 2, Right 3])
-- Right 6
-- >>> f (Left "hi" :| [Right 2])
-- Left "hi"
instance (Comonad set) => Choice (ValuationAlgebraOp set) where
  left' (ValuationAlgebraOp f) = ValuationAlgebraOp $ \seac ->
    case extract seac of
      Left a -> Left (f (fmap (fromLeft a) seac))
      Right c -> Right c
  right' (ValuationAlgebraOp f) = ValuationAlgebraOp $ \seca ->
    case extract seca of
      Right a -> Right (f (fmap (fromRight a) seca))
      Left c -> Left c

-- |
-- >>> import Data.Profunctor.Closed (Closed(..))
-- >>> let ValuationAlgebraOp f = closed (ValuationAlgebraOp sum :: ValuationAlgebraOp [] Int Int)
-- >>> f [(*2), (*3)] 10
-- 50
instance (Functor set) => Closed (ValuationAlgebraOp set) where
  closed (ValuationAlgebraOp f) = ValuationAlgebraOp (\sxa x -> f (fmap ($ x) sxa))

-- |
-- >>> import Data.Profunctor.Sieve (Cosieve(..))
-- >>> cosieve (ValuationAlgebraOp sum :: ValuationAlgebraOp [] Int Int) [1,2,3]
-- 6
instance (Functor set) => Cosieve (ValuationAlgebraOp set) set where
  cosieve (ValuationAlgebraOp f) = f

-- |
-- >>> import Data.Distributive (distribute)
-- >>> let ValuationAlgebraOp f = distribute [ValuationAlgebraOp sum, ValuationAlgebraOp product] :: ValuationAlgebraOp [] Int [Int]
-- >>> f [1,2,3]
-- [6,6]
instance Distributive (ValuationAlgebraOp set var) where
  distribute fs = ValuationAlgebraOp (\s -> fmap (\(ValuationAlgebraOp g) -> g s) fs)

-- |
-- >>> import Data.Functor.Rep (tabulate, index)
-- >>> let vao = tabulate (\s -> sum s * 2) :: ValuationAlgebraOp [] Int Int
-- >>> index vao [1,2,3]
-- 12
instance Representable (ValuationAlgebraOp set var) where
  type Rep (ValuationAlgebraOp set var) = set var
  tabulate = ValuationAlgebraOp
  index (ValuationAlgebraOp f) = f

-- |
-- >>> let ValuationAlgebraOp f = runSemigroup semigroupValuationAlgebraOp (ValuationAlgebraOp (const "hello")) (ValuationAlgebraOp (const " world") :: ValuationAlgebraOp [] Int String) in f []
-- "hello world"
semigroupValuationAlgebraOp :: (Prelude.Semigroup v) => Semigroup (ValuationAlgebraOp set var v)
semigroupValuationAlgebraOp = review applySemigroup (\(ValuationAlgebraOp f) (ValuationAlgebraOp g) -> ValuationAlgebraOp (\s -> f s <> g s))

-- |
-- >>> let ValuationAlgebraOp f = ValuationAlgebraOp (const "hello") <> (ValuationAlgebraOp (const " world") :: ValuationAlgebraOp [] Int String) in f []
-- "hello world"
instance (Prelude.Semigroup v) => Prelude.Semigroup (ValuationAlgebraOp set var v) where
  (<>) = runSemigroup semigroupValuationAlgebraOp

-- |
-- >>> let ValuationAlgebraOp f = mempty :: ValuationAlgebraOp [] Int String in f [1,2,3]
-- ""
instance (Prelude.Monoid v) => Prelude.Monoid (ValuationAlgebraOp set var v) where
  mempty = ValuationAlgebraOp (const mempty)