packages feed

valuations-0.0.3: src/Data/Valuation/ValuationAlgebra.hs

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

-- | A valuation algebra: a semi-valuation algebra with unit and zero operations.
module Data.Valuation.ValuationAlgebra
  ( ValuationAlgebra (..),
    ValuationAlgebra',
    SetValuationAlgebra,

    -- * optics
    HasValuationAlgebra (..),
    AsValuationAlgebra (..),
  )
where

import Control.Arrow (Arrow (..))
import Control.Category (Category (..))
import Control.Lens (Lens', Prism')
import Data.Functor.Contravariant (Contravariant (..))
import Data.Functor.Contravariant.Conclude (Conclude (..))
import Data.Functor.Contravariant.Decide (Decide (..))
import Data.Functor.Contravariant.Divise (Divise (..))
import Data.Functor.Contravariant.Divisible (Decidable (..), Divisible (..))
import Data.Profunctor (Profunctor (..), Strong (..))
import Data.Semigroupoid (Semigroupoid (..))
import Data.Set (Set)
import Data.Valuation.ProjectValuation (HasProjectValuation (..))
import Data.Valuation.SemiValuationAlgebra
  ( HasSemiValuationAlgebra (..),
    SemiValuationAlgebra,
  )
import Data.Valuation.Semigroup (HasSemigroup (..))
import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp (..))
import Witherable (Filterable (mapMaybe))
import Prelude hiding (Semigroup, id, (.))
import qualified Prelude

-- $setup
-- >>> :set -Wno-name-shadowing -Wno-type-defaults
-- >>> import Data.Void (Void)

-- |
-- >>> import Data.Valuation.SemiValuationAlgebra (SemiValuationAlgebra(..))
-- >>> import Data.Valuation.ProjectValuation (ProjectValuation(..))
-- >>> import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp(..))
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (Semigroup, applySemigroup, runSemigroup)
-- >>> let sva = SemiValuationAlgebra (review applySemigroup (+)) (ProjectValuation (\s v -> v + sum s))
-- >>> let va = ValuationAlgebra sva (ValuationAlgebraOp sum) (ValuationAlgebraOp (const 0)) :: ValuationAlgebra (->) Int [] Int
-- >>> let ValuationAlgebra (SemiValuationAlgebra sg (ProjectValuation p)) (ValuationAlgebraOp u) (ValuationAlgebraOp z) = va
-- >>> runSemigroup sg 3 4
-- 7
-- >>> p [1,2,3] 10
-- 16
-- >>> u [1,2,3]
-- 6
-- >>> z [1,2,3]
-- 0
data ValuationAlgebra p v set var
  = ValuationAlgebra
      (SemiValuationAlgebra p v set var)
      -- | algebra unit
      (ValuationAlgebraOp p set var v)
      -- | algebra zero
      (ValuationAlgebraOp p set var v)

type ValuationAlgebra' v set var =
  ValuationAlgebra (->) v set var

-- | Classy lens for types that contain a 'ValuationAlgebra'.
class HasValuationAlgebra c p v set var | c -> p v set var where
  valuationAlgebra :: Lens' c (ValuationAlgebra p v set var)
  valuationAlgebraUnit :: Lens' c (ValuationAlgebraOp p set var v)
  valuationAlgebraUnit = valuationAlgebra . valuationAlgebraUnit
  valuationAlgebraZero :: Lens' c (ValuationAlgebraOp p set var v)
  valuationAlgebraZero = valuationAlgebra . valuationAlgebraZero

instance HasValuationAlgebra (ValuationAlgebra p v set var) p v set var where
  valuationAlgebra = id
  valuationAlgebraUnit f (ValuationAlgebra s u z) = fmap (\u' -> ValuationAlgebra s u' z) (f u)
  valuationAlgebraZero f (ValuationAlgebra s u z) = fmap (ValuationAlgebra s u) (f z)

-- | Classy prism for types that can be constructed from a 'ValuationAlgebra'.
class AsValuationAlgebra c p v set var | c -> p v set var where
  _ValuationAlgebra :: Prism' c (ValuationAlgebra p v set var)

instance AsValuationAlgebra (ValuationAlgebra p v set var) p v set var where
  _ValuationAlgebra = id

instance HasSemiValuationAlgebra (ValuationAlgebra p v set var) p v set var where
  semiValuationAlgebra f (ValuationAlgebra a u z) = fmap (\a' -> ValuationAlgebra a' u z) (f a)

instance HasSemigroup (ValuationAlgebra p v set var) p v where
  semigroup = semiValuationAlgebra . semigroup

instance HasProjectValuation (ValuationAlgebra p v set var) p v set var where
  projectValuation = semiValuationAlgebra . projectValuation

-- |
-- >>> import Data.Functor.Contravariant (contramap)
-- >>> import Data.Valuation.SemiValuationAlgebra (SemiValuationAlgebra(..))
-- >>> import Data.Valuation.ProjectValuation (ProjectValuation(..))
-- >>> import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp(..))
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (Semigroup, applySemigroup, runSemigroup)
-- >>> let sva = SemiValuationAlgebra (review applySemigroup (+)) (ProjectValuation (\s v -> v + sum s))
-- >>> let va = ValuationAlgebra sva (ValuationAlgebraOp sum) (ValuationAlgebraOp (const 0)) :: ValuationAlgebra (->) Int [] Int
-- >>> let ValuationAlgebra (SemiValuationAlgebra sg (ProjectValuation p)) (ValuationAlgebraOp u) (ValuationAlgebraOp z) = contramap (*2) va
-- >>> runSemigroup sg 3 4
-- 7
-- >>> p [1,2,3] 10
-- 22
-- >>> u [1,2,3]
-- 12
-- >>> z [1,2,3]
-- 0
instance (Profunctor p, Functor set) => Contravariant (ValuationAlgebra p v set) where
  contramap f (ValuationAlgebra s (ValuationAlgebraOp u) (ValuationAlgebraOp z)) =
    ValuationAlgebra (contramap f s) (ValuationAlgebraOp (lmap (fmap f) u)) (ValuationAlgebraOp (lmap (fmap f) z))

-- |
-- >>> import Data.Functor.Contravariant.Divisible (conquer, divide)
-- >>> import Data.Valuation.SemiValuationAlgebra (SemiValuationAlgebra(..))
-- >>> import Data.Valuation.ProjectValuation (ProjectValuation(..))
-- >>> import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp(..))
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (Semigroup, applySemigroup, runSemigroup)
-- >>> let ValuationAlgebra (SemiValuationAlgebra sg (ProjectValuation p)) (ValuationAlgebraOp u) (ValuationAlgebraOp z) = conquer :: ValuationAlgebra (->) [Int] [] Int
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> p [10,20] [42]
-- [42]
-- >>> u [10,20]
-- []
-- >>> z [10,20]
-- []
--
-- >>> import Data.Functor.Contravariant.Divisible (conquer, divide)
-- >>> import Data.Valuation.SemiValuationAlgebra (SemiValuationAlgebra(..))
-- >>> import Data.Valuation.ProjectValuation (ProjectValuation(..))
-- >>> import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp(..))
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (Semigroup, applySemigroup, runSemigroup)
-- >>> let sva1 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ s))
-- >>> let va1 = ValuationAlgebra sva1 (ValuationAlgebraOp id) (ValuationAlgebraOp (map negate)) :: ValuationAlgebra (->) [Int] [] Int
-- >>> let sva2 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ reverse s))
-- >>> let va2 = ValuationAlgebra sva2 (ValuationAlgebraOp reverse) (ValuationAlgebraOp (const [])) :: ValuationAlgebra (->) [Int] [] Int
-- >>> let ValuationAlgebra (SemiValuationAlgebra sg (ProjectValuation p)) (ValuationAlgebraOp u) (ValuationAlgebraOp z) = divide (\x -> (x, x + 10)) va1 va2
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> u [1,2,3]
-- [1,2,3,13,12,11]
-- >>> z [1,2,3]
-- [-1,-2,-3]
instance (Functor set, Strong p, Arrow p, Prelude.Semigroup v, Prelude.Monoid v) => Divisible (ValuationAlgebra p v set) where
  conquer = ValuationAlgebra conquer (ValuationAlgebraOp (rmap (const mempty) id)) (ValuationAlgebraOp (rmap (const mempty) id))
  divide f (ValuationAlgebra s1 (ValuationAlgebraOp u1) (ValuationAlgebraOp z1)) (ValuationAlgebra s2 (ValuationAlgebraOp u2) (ValuationAlgebraOp z2)) =
    let combine g1 g2 =
          let g1' = lmap (fmap (fst . f)) g1
              g2' = lmap (fmap (snd . f)) g2
           in ValuationAlgebraOp (lmap (\x -> (x, x)) (rmap (uncurry (<>)) (second' g2' . first' g1')))
     in ValuationAlgebra (divide f s1 s2) (combine u1 u2) (combine z1 z2)

-- |
-- >>> import Data.Functor.Contravariant.Divisible (choose, lose)
-- >>> import Data.Void (Void, absurd)
-- >>> import Data.Valuation.SemiValuationAlgebra (SemiValuationAlgebra(..))
-- >>> import Data.Valuation.ProjectValuation (ProjectValuation(..))
-- >>> import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp(..))
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (Semigroup, applySemigroup, runSemigroup)
-- >>> let ValuationAlgebra (SemiValuationAlgebra sg (ProjectValuation p)) (ValuationAlgebraOp u) (ValuationAlgebraOp z) = lose absurd :: ValuationAlgebra (->) [Int] [] Void
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> p [] [42]
-- [42]
-- >>> u []
-- []
-- >>> z []
-- []
--
-- >>> import Data.Functor.Contravariant.Divisible (choose)
-- >>> import Data.Valuation.SemiValuationAlgebra (SemiValuationAlgebra(..))
-- >>> import Data.Valuation.ProjectValuation (ProjectValuation(..))
-- >>> import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp(..))
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (Semigroup, applySemigroup, runSemigroup)
-- >>> let sva1 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ s))
-- >>> let va1 = ValuationAlgebra sva1 (ValuationAlgebraOp id) (ValuationAlgebraOp (map negate)) :: ValuationAlgebra (->) [Int] [] Int
-- >>> let sva2 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ reverse s))
-- >>> let va2 = ValuationAlgebra sva2 (ValuationAlgebraOp reverse) (ValuationAlgebraOp (const [])) :: ValuationAlgebra (->) [Int] [] Int
-- >>> let ValuationAlgebra (SemiValuationAlgebra sg (ProjectValuation p)) (ValuationAlgebraOp u) (ValuationAlgebraOp z) = choose (\x -> if even x then Left x else Right x) va1 va2
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> u [1,2,3,4]
-- [2,4,3,1]
-- >>> z [1,2,3,4]
-- [-2,-4]
instance (Filterable set, Strong p, Arrow p, Prelude.Semigroup v, Prelude.Monoid v) => Decidable (ValuationAlgebra p v set) where
  lose f = ValuationAlgebra (lose f) (ValuationAlgebraOp (rmap (const mempty) id)) (ValuationAlgebraOp (rmap (const mempty) id))
  choose ch (ValuationAlgebra s1 (ValuationAlgebraOp u1) (ValuationAlgebraOp z1)) (ValuationAlgebra s2 (ValuationAlgebraOp u2) (ValuationAlgebraOp z2)) =
    let lefts = mapMaybe (either Just (const Nothing) . ch)
        rights = mapMaybe (either (const Nothing) Just . ch)
        combine g1 g2 =
          let g1' = lmap lefts g1
              g2' = lmap rights g2
           in ValuationAlgebraOp (lmap (\x -> (x, x)) (rmap (uncurry (<>)) (second' g2' . first' g1')))
     in ValuationAlgebra (choose ch s1 s2) (combine u1 u2) (combine z1 z2)

-- |
-- >>> import Data.Functor.Contravariant.Divise (divise)
-- >>> import Data.Valuation.SemiValuationAlgebra (SemiValuationAlgebra(..))
-- >>> import Data.Valuation.ProjectValuation (ProjectValuation(..))
-- >>> import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp(..))
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (Semigroup, applySemigroup, runSemigroup)
-- >>> let sva1 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ s))
-- >>> let va1 = ValuationAlgebra sva1 (ValuationAlgebraOp id) (ValuationAlgebraOp (map negate)) :: ValuationAlgebra (->) [Int] [] Int
-- >>> let sva2 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ reverse s))
-- >>> let va2 = ValuationAlgebra sva2 (ValuationAlgebraOp reverse) (ValuationAlgebraOp (const [])) :: ValuationAlgebra (->) [Int] [] Int
-- >>> let ValuationAlgebra (SemiValuationAlgebra sg (ProjectValuation p)) (ValuationAlgebraOp u) (ValuationAlgebraOp z) = divise (\x -> (x, x + 10)) va1 va2
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> u [1,2,3]
-- [1,2,3,13,12,11]
-- >>> z [1,2,3]
-- [-1,-2,-3]
instance (Functor set, Strong p, Semigroupoid p, Prelude.Semigroup v) => Divise (ValuationAlgebra p v set) where
  divise f (ValuationAlgebra s1 (ValuationAlgebraOp u1) (ValuationAlgebraOp z1)) (ValuationAlgebra s2 (ValuationAlgebraOp u2) (ValuationAlgebraOp z2)) =
    let combine g1 g2 =
          let g1' = lmap (fmap (fst . f)) g1
              g2' = lmap (fmap (snd . f)) g2
           in ValuationAlgebraOp (lmap (\x -> (x, x)) (rmap (uncurry (<>)) (second' g2' `o` first' g1')))
     in ValuationAlgebra (divise f s1 s2) (combine u1 u2) (combine z1 z2)

-- |
-- >>> import Data.Functor.Contravariant.Decide (decide)
-- >>> import Data.Valuation.SemiValuationAlgebra (SemiValuationAlgebra(..))
-- >>> import Data.Valuation.ProjectValuation (ProjectValuation(..))
-- >>> import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp(..))
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (Semigroup, applySemigroup, runSemigroup)
-- >>> let sva1 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ s))
-- >>> let va1 = ValuationAlgebra sva1 (ValuationAlgebraOp id) (ValuationAlgebraOp (map negate)) :: ValuationAlgebra (->) [Int] [] Int
-- >>> let sva2 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ reverse s))
-- >>> let va2 = ValuationAlgebra sva2 (ValuationAlgebraOp reverse) (ValuationAlgebraOp (const [])) :: ValuationAlgebra (->) [Int] [] Int
-- >>> let ValuationAlgebra (SemiValuationAlgebra sg (ProjectValuation p)) (ValuationAlgebraOp u) (ValuationAlgebraOp z) = decide (\x -> if even x then Left x else Right x) va1 va2
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> u [1,2,3,4]
-- [2,4,3,1]
-- >>> z [1,2,3,4]
-- [-2,-4]
instance (Filterable set, Strong p, Semigroupoid p, Prelude.Semigroup v) => Decide (ValuationAlgebra p v set) where
  decide ch (ValuationAlgebra s1 (ValuationAlgebraOp u1) (ValuationAlgebraOp z1)) (ValuationAlgebra s2 (ValuationAlgebraOp u2) (ValuationAlgebraOp z2)) =
    let lefts = mapMaybe (either Just (const Nothing) . ch)
        rights = mapMaybe (either (const Nothing) Just . ch)
        combine g1 g2 =
          let g1' = lmap lefts g1
              g2' = lmap rights g2
           in ValuationAlgebraOp (lmap (\x -> (x, x)) (rmap (uncurry (<>)) (second' g2' `o` first' g1')))
     in ValuationAlgebra (decide ch s1 s2) (combine u1 u2) (combine z1 z2)

-- |
-- >>> import Data.Functor.Contravariant.Conclude (conclude)
-- >>> import Data.Void (absurd)
-- >>> import Data.Valuation.SemiValuationAlgebra (SemiValuationAlgebra(..))
-- >>> import Data.Valuation.ProjectValuation (ProjectValuation(..))
-- >>> import Data.Valuation.ValuationAlgebraOp (ValuationAlgebraOp(..))
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (Semigroup, applySemigroup, runSemigroup)
-- >>> let ValuationAlgebra (SemiValuationAlgebra sg (ProjectValuation p)) (ValuationAlgebraOp u) (ValuationAlgebraOp z) = conclude absurd :: ValuationAlgebra (->) [Int] [] Void
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> u []
-- []
-- >>> z []
-- []
instance (Filterable set, Strong p, Semigroupoid p, Arrow p, Prelude.Semigroup v, Prelude.Monoid v) => Conclude (ValuationAlgebra p v set) where
  conclude f = ValuationAlgebra (conclude f) (ValuationAlgebraOp (rmap (const mempty) id)) (ValuationAlgebraOp (rmap (const mempty) id))

-- | A 'ValuationAlgebra' specialised to 'Set'.
type SetValuationAlgebra p v var =
  ValuationAlgebra p v Set var