packages feed

hedgehog-optics-1.0.0.3: src/Hedgehog/Optics.hs

module Hedgehog.Optics
  ( wellFormedPrism,
    wellFormedLens,
    wellFormedIso,
    prismExample,
  )
where

import Control.Monad (Monad (return))
import Data.Either (Either (Left, Right))
import Data.Eq (Eq)
import Data.Function ((.))
import Data.Maybe (Maybe (Just))
import Hedgehog (Gen, PropertyT, annotate, forAll, (===))
import Optics.AffineFold (preview)
import Optics.AffineTraversal (matching)
import Optics.Getter (view)
import Optics.Iso (Iso')
import Optics.Lens (Lens')
import Optics.Prism (Prism')
import Optics.Review (review)
import Optics.Setter (set)
import Text.Show (Show)

-- | Checks whether a prism respects the well-formedness
--    laws given in "Optics.Prism"
wellFormedPrism ::
  Monad m =>
  (Show large, Eq large) =>
  (Show small, Eq small) =>
  Gen large ->
  Gen small ->
  -- | Prism signifying that the @small@ type
  --   is a subset of the @large@ type -}
  Prism' large small ->
  PropertyT m ()
wellFormedPrism genLarge genSmall o = do
  getSetPrismLaw genLarge o
  setGetPrismLaw genSmall o

getSetPrismLaw ::
  Monad m =>
  (Show large, Eq large) =>
  (Show small, Eq small) =>
  Gen large ->
  Prism' large small ->
  PropertyT m ()
getSetPrismLaw genLarge o = do
  large <- forAll genLarge
  case matching o large of
    Right small -> do
      annotate "The get-set law must hold for a Prism"
      review o small === large
    Left _ -> return ()

setGetPrismLaw ::
  Monad m =>
  (Show large, Eq large) =>
  (Show small, Eq small) =>
  Gen small ->
  Prism' large small ->
  PropertyT m ()
setGetPrismLaw genSmall o = do
  small <- forAll genSmall
  annotate "The set-get law must hold for a Prism"
  matching o (review o small) === Right small

-- | Checks whether a lens respects the well-formedness
--    laws given in "Optics.Lens"
wellFormedLens ::
  Monad m =>
  (Show large, Eq large) =>
  (Show small, Eq small) =>
  Gen large ->
  Gen small ->
  -- | Lens signifying that the @small@ type is
  --   a constituent part of the @large@ type
  Lens' large small ->
  PropertyT m ()
wellFormedLens genLarge genSmall o = do
  getPutLensLaw genLarge genSmall o
  putGetLensLaw genLarge o
  putPutLensLaw genLarge genSmall o

getPutLensLaw ::
  Monad m =>
  (Show large, Eq large) =>
  (Show small, Eq small) =>
  Gen large ->
  Gen small ->
  Lens' large small ->
  PropertyT m ()
getPutLensLaw genLarge genSmall o = do
  large <- forAll genLarge
  small <- forAll genSmall
  annotate "The set-get law must hold for a Lens"
  view o (set o small large) === small

putGetLensLaw ::
  Monad m =>
  (Show large, Eq large) =>
  (Show small, Eq small) =>
  Gen large ->
  Lens' large small ->
  PropertyT m ()
putGetLensLaw genLarge o = do
  large <- forAll genLarge
  annotate "The get-set law must hold for a Lens"
  set o (view o large) large === large

putPutLensLaw ::
  Monad m =>
  (Show large, Eq large) =>
  (Show small, Eq small) =>
  Gen large ->
  Gen small ->
  Lens' large small ->
  PropertyT m ()
putPutLensLaw genLarge genSmall o = do
  large <- forAll genLarge
  small1 <- forAll genSmall
  small2 <- forAll genSmall
  annotate "The set-set law must hold for a Lens"
  set o small2 (set o small1 large) === set o small2 large

-- | Checks whether an isomorphism respects the
--    well-formedness laws given in "Optics.Iso"
wellFormedIso ::
  Monad m =>
  (Show a, Eq a) =>
  (Show b, Eq b) =>
  Gen a ->
  Gen b ->
  -- | Isomorphism signifying that types
  --   @a@ and @b@ are basically the same thing
  Iso' a b ->
  PropertyT m ()
wellFormedIso genA genB o = do
  setGetIsoLaw genB o
  getSetIsoLaw genA o

setGetIsoLaw ::
  Monad m =>
  (Show a, Eq a) =>
  (Show b, Eq b) =>
  Gen b ->
  Iso' a b ->
  PropertyT m ()
setGetIsoLaw genB o = do
  b <- forAll genB
  annotate "The set-get law must hold for an Iso"
  (view o . review o) b === b

getSetIsoLaw ::
  Monad m =>
  (Show a, Eq a) =>
  (Show b, Eq b) =>
  Gen a ->
  Iso' a b ->
  PropertyT m ()
getSetIsoLaw genA o = do
  a <- forAll genA
  annotate "The get-set law must hold for an Iso"
  (review o . view o) a === a

-- | Assert that a prism matches for a particular set of values
--
-- A 'review' of the @small@ value should produce the @large@ value, and
-- a 'preview' of the @large@ value should produce the @small@ value.
prismExample ::
  Monad m =>
  (Show large, Eq large) =>
  (Show small, Eq small) =>
  -- | Prism signifying that the @small@
  --   type is a subset of the @large@ type
  Prism' large small ->
  large ->
  small ->
  PropertyT m ()
prismExample o large small = do
  review o small === large
  preview o large === Just small