packages feed

valuations-0.0.1: 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 (..),
    SetSemiValuationAlgebra,

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

    -- * combinators
    projectValuation',
  )
where

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.Set (Set)
import Data.Valuation.ProjectValuation
  ( HasProjectValuation (..),
    ProjectValuation (..),
  )
import Data.Valuation.Semigroup
  ( HasSemigroup (..),
    Semigroup,
    applySemigroup,
  )
import Witherable (Filterable)
import Prelude hiding (Semigroup)
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 v set var
  = SemiValuationAlgebra
      -- | algebra combine
      (Semigroup v)
      -- | algebra project
      (ProjectValuation v set var)

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

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

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

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

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

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

instance HasProjectValuation (SemiValuationAlgebra v set var) 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) => Contravariant (SemiValuationAlgebra 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, Prelude.Semigroup v) => Divisible (SemiValuationAlgebra v set) where
  conquer = SemiValuationAlgebra (review applySemigroup (<>)) 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, Prelude.Semigroup v) => Decidable (SemiValuationAlgebra v set) where
  lose f = SemiValuationAlgebra (review applySemigroup (<>)) (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) => Divise (SemiValuationAlgebra 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) => Decide (SemiValuationAlgebra 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, Prelude.Semigroup v) => Conclude (SemiValuationAlgebra v set) where
  conclude f = SemiValuationAlgebra (review applySemigroup (<>)) (conclude f)

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