packages feed

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

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

-- | A projection function that updates a value given a set of variables.
module Data.Valuation.ProjectValuation
  ( ProjectValuation (..),
    SetProjectValuation,

    -- * optics
    HasProjectValuation (..),
    AsProjectValuation (..),

    -- * combinators
    semigroupProjectValuation,
    applyHasProjectValuation,
    applyAsProjectValuation,
  )
where

import Control.Lens
  ( Lens',
    Prism',
    Rewrapped,
    Wrapped (..),
    iso,
    review,
    _Wrapped,
  )
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.Semigroup
  ( Semigroup,
    applySemigroup,
    runSemigroup,
  )
import Witherable (Filterable (mapMaybe))
import Prelude hiding (Semigroup)
import qualified Prelude

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

-- |
-- >>> let ProjectValuation f = ProjectValuation (\s v -> v + sum s) in f [1,2,3] (10 :: Int)
-- 16
--
-- >>> let ProjectValuation f = ProjectValuation (\s v -> v + length s) in f [1,2,3] (10 :: Int)
-- 13
newtype ProjectValuation v set var
  = ProjectValuation (set var -> v -> v)

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

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

-- | Classy lens for types that contain a 'ProjectValuation'.
class HasProjectValuation c v set var | c -> v set var where
  projectValuation :: Lens' c (ProjectValuation v set var)

instance HasProjectValuation (ProjectValuation v set var) v set var where
  projectValuation = id

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

instance AsProjectValuation (ProjectValuation v set var) v set var where
  _ProjectValuation = id

-- | Lens to the underlying function of a 'HasProjectValuation'.
applyHasProjectValuation :: (HasProjectValuation pv v set var) => Lens' pv (set var -> v -> v)
applyHasProjectValuation = projectValuation . _Wrapped

-- | Prism to the underlying function of an 'AsProjectValuation'.
applyAsProjectValuation :: (AsProjectValuation pv v set var) => Prism' pv (set var -> v -> v)
applyAsProjectValuation = _ProjectValuation . _Wrapped

-- |
-- >>> import Data.Functor.Contravariant (contramap)
-- >>> let pv = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = contramap (*2) pv in f [1,2,3] 10
-- 22
--
-- >>> import Data.Functor.Contravariant (contramap)
-- >>> let pv = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = contramap negate pv in f [1,2,3] 0
-- -6
instance (Functor set) => Contravariant (ProjectValuation v set) where
  contramap f (ProjectValuation g) = ProjectValuation (g . fmap f)

-- |
-- >>> import Data.Functor.Contravariant.Divisible (conquer, divide)
-- >>> let ProjectValuation f = conquer :: ProjectValuation Int [] Int in f [1,2,3] 42
-- 42
--
-- >>> import Data.Functor.Contravariant.Divisible (conquer, divide)
-- >>> let ProjectValuation f = conquer :: ProjectValuation String [] Char in f "abc" "hello"
-- "hello"
--
-- >>> import Data.Functor.Contravariant.Divisible (conquer, divide)
-- >>> let pvB = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let pvC = ProjectValuation (\s v -> v * length s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = divide (\x -> (x, x * 10)) pvB pvC in f [1,2,3] 5
-- 21
--
-- >>> import Data.Functor.Contravariant.Divisible (conquer, divide)
-- >>> let pvB = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let pvC = ProjectValuation (\s v -> v * length s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = divide (\x -> (x, x)) pvB pvC in f [1,2,3] 5
-- 21
instance (Functor set) => Divisible (ProjectValuation v set) where
  conquer = ProjectValuation (const id)
  divide split (ProjectValuation pb) (ProjectValuation pc) =
    ProjectValuation (\fa v -> pb (fmap (fst . split) fa) (pc (fmap (snd . split) fa) v))

-- |
-- >>> import Data.Functor.Contravariant.Divisible (choose, lose)
-- >>> import Data.Void (Void, absurd)
-- >>> let ProjectValuation f = lose absurd :: ProjectValuation Int [] Void in f [] 42
-- 42
--
-- >>> import Data.Functor.Contravariant.Divisible (choose)
-- >>> let pvB = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let pvC = ProjectValuation (\s v -> v * length s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = choose (\x -> if even x then Left x else Right x) pvB pvC in f [1,2,3,4] 10
-- 26
--
-- >>> import Data.Functor.Contravariant.Divisible (choose)
-- >>> let pvB = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let pvC = ProjectValuation (\s v -> v * length s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = choose Left pvB pvC in f [1,2,3] 10
-- 6
--
-- >>> import Data.Functor.Contravariant.Divisible (choose)
-- >>> let pvB = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let pvC = ProjectValuation (\s v -> v * length s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = choose Right pvB pvC in f [1,2,3] 10
-- 30
instance (Filterable set) => Decidable (ProjectValuation v set) where
  lose _ = ProjectValuation (const id)
  choose ch (ProjectValuation pb) (ProjectValuation pc) =
    ProjectValuation
      ( \fa v ->
          let fb = mapMaybe (either Just (const Nothing) . ch) fa
              fc = mapMaybe (either (const Nothing) Just . ch) fa
           in pb fb (pc fc v)
      )

-- |
-- >>> import Data.Functor.Contravariant.Divise (divise)
-- >>> let pvB = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let pvC = ProjectValuation (\s v -> v * length s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = divise (\x -> (x, x * 10)) pvB pvC in f [1,2,3] 5
-- 21
instance (Functor set) => Divise (ProjectValuation v set) where
  divise = divide

-- |
-- >>> import Data.Functor.Contravariant.Decide (decide)
-- >>> let pvB = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let pvC = ProjectValuation (\s v -> v * length s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = decide (\x -> if even x then Left x else Right x) pvB pvC in f [1,2,3,4] 10
-- 26
instance (Filterable set) => Decide (ProjectValuation v set) where
  decide = choose

-- |
-- >>> import Data.Functor.Contravariant.Conclude (conclude)
-- >>> import Data.Void (absurd)
-- >>> let ProjectValuation f = conclude absurd :: ProjectValuation Int [] Void in f [] 42
-- 42
instance (Filterable set) => Conclude (ProjectValuation v set) where
  conclude _ = ProjectValuation (const id)

-- |
-- >>> let ProjectValuation f = runSemigroup semigroupProjectValuation (ProjectValuation (\_ v -> v + 1)) (ProjectValuation (\_ v -> v * 2)) in f [] (3 :: Int)
-- 7
semigroupProjectValuation :: Semigroup (ProjectValuation v set var)
semigroupProjectValuation = review applySemigroup (\(ProjectValuation p1) (ProjectValuation p2) -> ProjectValuation (\s -> p1 s . p2 s))

-- |
-- >>> let p1 = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let p2 = ProjectValuation (\s v -> v * length s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = p1 <> p2 in f [1,2,3] 5
-- 21
instance Prelude.Semigroup (ProjectValuation v set var) where
  (<>) = runSemigroup semigroupProjectValuation

-- |
-- >>> let p = ProjectValuation (\s v -> v + sum s) :: ProjectValuation Int [] Int
-- >>> let ProjectValuation f = mempty <> p in f [1,2,3] 5
-- 11
instance Monoid (ProjectValuation v set var) where
  mempty = ProjectValuation (const id)

-- | A 'ProjectValuation' specialised to 'Set'.
type SetProjectValuation v var =
  ProjectValuation v Set var