nat-optics-1.0.0.3: src/NatOptics/NonNegative.hs
{-# language Trustworthy #-}
module NatOptics.NonNegative
(
{- * Type constructor -} NonNegative,
{- * Optics -} refine, natPrism, intPrism,
natIso, textPrism, stringPrism,
{- * Re-exports -} Natural, Integer, Prism', Iso',
view, review, preview,
) where
import Control.Applicative ( (*>) )
import Control.Monad ( guard )
import Data.Bits ( Bits, toIntegralSized )
import Data.Function ( (.) )
import Data.Functor ( fmap, ($>), (<$>) )
import Data.Maybe ( Maybe )
import Data.Ord ( Ord, (>=) )
import Data.String ( String )
import Data.Text ( Text )
import NatOptics.Internal ( strNat, textStr )
import Numeric.Natural ( Natural )
import Optics.AffineFold ( preview )
import Optics.Getter ( view )
import Optics.Iso ( Iso', iso )
import Optics.Optic ( (%) )
import Optics.Prism ( Prism', prism' )
import Optics.Review ( review )
import Prelude ( Integer, Integral, Num,
fromIntegral, toInteger )
import NatOptics.NonNegative.Unsafe (NonNegative (..))
{- | For any numeric type @n@,
@'NonNegative' n@ is a subset of @n@.
Examples:
- @'preview' 'refine' (-1 :: 'Integer')@ = @'Nothing'@
- @'preview' 'refine' (0 :: 'Integer')@ = @'Just' (NonNegative 0)@
- @'preview' 'refine' (1 :: 'Integer')@ = @'Just' (NonNegative 1)@
- @'preview' 'refine' (2 :: 'Integer')@ = @'Just' (NonNegative 2)@
-}
refine :: (Num n, Ord n) => Prism' n (NonNegative n)
refine = prism' number verify
{- | For any integral type @n@,
@'NonNegative' n@ is a subset of 'Natural'. -}
natPrism :: (Integral n, Bits n) => Prism' Natural (NonNegative n)
natPrism =
prism'
(fromIntegral . number)
(fmap NonNegativeUnsafe . toIntegralSized) {- No need to verify
here, because Natural is always non-negative. The only
check here is when converting from 'Natural' to ensure
that it does not overflow the max bound of 'n'. -}
{- | For any integral type @n@,
@'NonNegative' n@ is a subset of 'Integer'. -}
intPrism :: (Integral n, Bits n) => Prism' Integer (NonNegative n)
intPrism = prism' (toInteger . number) verifyAndResize
{- | 'Natural' and @'NonNegative' 'Natural'@ are the same thing. -}
natIso :: Iso' Natural (NonNegative Natural)
natIso = iso NonNegativeUnsafe number
stringPrism :: (Integral n, Bits n) => Prism' String (NonNegative n)
stringPrism = strNat % natPrism
textPrism :: (Integral n, Bits n) => Prism' Text (NonNegative n)
textPrism = textStr % stringPrism
verify :: (Ord n, Num n) => n -> Maybe (NonNegative n)
verify n = guard (n >= 0) $> NonNegativeUnsafe n
verifyAndResize :: (Integral a, Integral b, Bits a, Bits b)
=> a -> Maybe (NonNegative b)
verifyAndResize x = verify x *> (NonNegativeUnsafe <$> toIntegralSized x)