packages feed

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

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

-- | A semi-valuation algebra: a semigroup paired with a projection.
module Data.Valuation.SemiValuationAlgebra
  ( SemiValuationAlgebra (..),
    SemiValuationAlgebra',
    SetSemiValuationAlgebra,

    -- * optics
    HasSemiValuationAlgebra (..),
    AsSemiValuationAlgebra (..),

    -- * combinators
    projectValuation',
  )
where

import Control.Arrow (Arrow (..))
import Control.Category (Category (..))
import Control.Lens
  ( Lens,
    Lens',
    Prism',
    review,
  )
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 (..),
    ProjectValuation (..),
  )
import Data.Valuation.Semigroup
  ( HasSemigroup (..),
    Semigroup,
    applySemigroup,
  )
import Witherable (Filterable)
import Prelude hiding (Semigroup, id, (.))
import qualified Prelude

-- $setup
-- >>> :set -Wno-name-shadowing -Wno-type-defaults
-- >>> import Control.Lens (review)
-- >>> import Data.Valuation.Semigroup (applySemigroup, runSemigroup)
-- >>> import Data.Void (Void)
-- >>> import Prelude hiding (Semigroup)

-- |
-- >>> let SemiValuationAlgebra sg (ProjectValuation p) = SemiValuationAlgebra (review applySemigroup (+)) (ProjectValuation (\s v -> v + sum s)) :: SemiValuationAlgebra (->) Int [] Int
-- >>> runSemigroup sg 3 4
-- 7
-- >>> p [1,2,3] 10
-- 16
data SemiValuationAlgebra p v set var
  = SemiValuationAlgebra
      -- | algebra combine
      (Semigroup p v)
      -- | algebra project
      (ProjectValuation p v set var)

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

-- | Type-changing lens to the 'ProjectValuation' component.
projectValuation' :: Lens (SemiValuationAlgebra p v set var) (SemiValuationAlgebra p v set' var') (ProjectValuation p v set var) (ProjectValuation p v set' var')
projectValuation' f (SemiValuationAlgebra s p) = fmap (SemiValuationAlgebra s) (f p)

-- | Classy lens for types that contain a 'SemiValuationAlgebra'.
class HasSemiValuationAlgebra c p v set var | c -> p v set var where
  semiValuationAlgebra ::
    Lens' c (SemiValuationAlgebra p v set var)

instance HasSemiValuationAlgebra (SemiValuationAlgebra p v set var) p v set var where
  semiValuationAlgebra = id

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

instance AsSemiValuationAlgebra (SemiValuationAlgebra p v set var) p v set var where
  _SemiValuationAlgebra = id

instance HasSemigroup (SemiValuationAlgebra p v set var) p v where
  semigroup f (SemiValuationAlgebra s p) = fmap (`SemiValuationAlgebra` p) (f s)

instance HasProjectValuation (SemiValuationAlgebra p v set var) p v set var where
  projectValuation = projectValuation'

-- |
-- >>> import Data.Functor.Contravariant (contramap)
-- >>> let sva = SemiValuationAlgebra (review applySemigroup (+)) (ProjectValuation (\s v -> v + sum s)) :: SemiValuationAlgebra (->) Int [] Int
-- >>> let SemiValuationAlgebra sg (ProjectValuation p) = contramap (*2) sva
-- >>> runSemigroup sg 3 4
-- 7
-- >>> p [1,2,3] 10
-- 22
instance (Functor set, Profunctor p) => Contravariant (SemiValuationAlgebra p v set) where
  contramap f (SemiValuationAlgebra s p) = SemiValuationAlgebra s (contramap f p)

-- |
-- >>> import Data.Functor.Contravariant.Divisible (conquer, divide)
-- >>> let SemiValuationAlgebra sg (ProjectValuation p) = conquer :: SemiValuationAlgebra (->) [Int] [] Int
-- >>> runSemigroup sg [1,2] [3,4]
-- [1,2,3,4]
-- >>> p [10,20,30] [42]
-- [42]
--
-- >>> import Data.Functor.Contravariant.Divisible (conquer, divide)
-- >>> let sva1 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ s)) :: SemiValuationAlgebra (->) [Int] [] Int
-- >>> let sva2 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ reverse s)) :: SemiValuationAlgebra (->) [Int] [] Int
-- >>> let SemiValuationAlgebra sg (ProjectValuation p) = divide (\x -> (x, x + 10)) sva1 sva2
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> p [1,2,3] [0]
-- [0,13,12,11,1,2,3]
instance (Functor set, Strong p, Arrow p, Prelude.Semigroup v) => Divisible (SemiValuationAlgebra p v set) where
  conquer = SemiValuationAlgebra (review applySemigroup (rmap arr (arr (<>)))) conquer
  divide f (SemiValuationAlgebra s p1) (SemiValuationAlgebra _ p2) =
    SemiValuationAlgebra s (divide f p1 p2)

-- |
-- >>> import Data.Functor.Contravariant.Divisible (choose, lose)
-- >>> import Data.Void (Void, absurd)
-- >>> let SemiValuationAlgebra sg (ProjectValuation p) = lose absurd :: SemiValuationAlgebra (->) [Int] [] Void
-- >>> runSemigroup sg [1,2] [3,4]
-- [1,2,3,4]
-- >>> p [] [42]
-- [42]
--
-- >>> import Data.Functor.Contravariant.Divisible (choose)
-- >>> let sva1 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ s)) :: SemiValuationAlgebra (->) [Int] [] Int
-- >>> let sva2 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ map negate s)) :: SemiValuationAlgebra (->) [Int] [] Int
-- >>> let SemiValuationAlgebra sg (ProjectValuation p) = choose (\x -> if even x then Left x else Right x) sva1 sva2
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> p [1,2,3,4] [0]
-- [0,-1,-3,2,4]
instance (Filterable set, Strong p, Arrow p, Prelude.Semigroup v) => Decidable (SemiValuationAlgebra p v set) where
  lose f = SemiValuationAlgebra (review applySemigroup (rmap arr (arr (<>)))) (lose f)
  choose f (SemiValuationAlgebra s p1) (SemiValuationAlgebra _ p2) =
    SemiValuationAlgebra s (choose f p1 p2)

-- |
-- >>> import Data.Functor.Contravariant.Divise (divise)
-- >>> let sva1 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ s)) :: SemiValuationAlgebra (->) [Int] [] Int
-- >>> let sva2 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ reverse s)) :: SemiValuationAlgebra (->) [Int] [] Int
-- >>> let SemiValuationAlgebra sg (ProjectValuation p) = divise (\x -> (x, x + 10)) sva1 sva2
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> p [1,2,3] [0]
-- [0,13,12,11,1,2,3]
instance (Functor set, Strong p, Semigroupoid p) => Divise (SemiValuationAlgebra p v set) where
  divise f (SemiValuationAlgebra s p1) (SemiValuationAlgebra _ p2) =
    SemiValuationAlgebra s (divise f p1 p2)

-- |
-- >>> import Data.Functor.Contravariant.Decide (decide)
-- >>> let sva1 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ s)) :: SemiValuationAlgebra (->) [Int] [] Int
-- >>> let sva2 = SemiValuationAlgebra (review applySemigroup (++)) (ProjectValuation (\s v -> v ++ map negate s)) :: SemiValuationAlgebra (->) [Int] [] Int
-- >>> let SemiValuationAlgebra sg (ProjectValuation p) = decide (\x -> if even x then Left x else Right x) sva1 sva2
-- >>> runSemigroup sg [1] [2]
-- [1,2]
-- >>> p [1,2,3,4] [0]
-- [0,-1,-3,2,4]
instance (Filterable set, Strong p, Semigroupoid p) => Decide (SemiValuationAlgebra p v set) where
  decide f (SemiValuationAlgebra s p1) (SemiValuationAlgebra _ p2) =
    SemiValuationAlgebra s (decide f p1 p2)

-- |
-- >>> import Data.Functor.Contravariant.Conclude (conclude)
-- >>> import Data.Void (absurd)
-- >>> let SemiValuationAlgebra sg (ProjectValuation p) = conclude absurd :: SemiValuationAlgebra (->) [Int] [] Void
-- >>> runSemigroup sg [1,2] [3,4]
-- [1,2,3,4]
-- >>> p [] [42]
-- [42]
instance (Filterable set, Strong p, Semigroupoid p, Arrow p, Prelude.Semigroup v) => Conclude (SemiValuationAlgebra p v set) where
  conclude f = SemiValuationAlgebra (review applySemigroup (rmap arr (arr (<>)))) (conclude f)

-- | A 'SemiValuationAlgebra' specialised to 'Set'.
type SetSemiValuationAlgebra p v var =
  SemiValuationAlgebra p v Set var