packages feed

associative-0.0.1: examples/Data/Associative/Examples/PartialSemigroupOpExamples.hs

{-# OPTIONS_GHC -Wall -Werror #-}

-- |
-- Example usages of "Data.Associative.PartialSemigroupOp".
module Data.Associative.Examples.PartialSemigroupOpExamples
  ( -- * Constructing and running partial semigroups
    addPositive,
    multiply,

    -- * Combining operations with Semigroup (first-success fallback)
    fallbackExample,

    -- * Functor
    fmapPartialExample,

    -- * Using effects (monad transformer)
    countedAdd,
  )
where

import Control.Monad.State (State, get, put)
import Data.Associative.PartialSemigroupOp
  ( PartialSemigroupOp',
    PartialSemigroupOpT (..),
    runPartialSemigroupOp,
    runPartialSemigroupOpT,
    total,
  )
import Data.Functor.Identity (Identity (..))
import Prelude hiding (null)

-- $setup
-- >>> import Data.Functor.Identity (Identity(..))
-- >>> import Control.Monad.State (runState)

-- * Constructing and running partial semigroups

-- | A partial semigroup that adds two positive integers,
-- returning 'Nothing' when either input is non-positive.
--
-- >>> runPartialSemigroupOp addPositive 3 4
-- Just 7
-- >>> runPartialSemigroupOp addPositive (-1) 4
-- Nothing
-- >>> runPartialSemigroupOp addPositive 0 5
-- Nothing
addPositive :: PartialSemigroupOp' Int
addPositive =
  PartialSemigroupOpT
    ( \a b ->
        Identity
          ( if a > 0 && b > 0
              then Just (a + b)
              else Nothing
          )
    )

-- | A partial semigroup built with the 'total' smart constructor.
-- Total operations always succeed (always return 'Just').
--
-- >>> runPartialSemigroupOp multiply 3 4
-- Just 12
-- >>> runPartialSemigroupOp multiply 0 5
-- Just 0
multiply :: PartialSemigroupOp' Int
multiply = total (*)

-- * Combining operations with Semigroup (first-success fallback)

-- | Partial semigroups combine with first-success fallback:
-- try the left operand; if it returns 'Nothing', try the right.
--
-- >>> runPartialSemigroupOp (addPositive <> total (+)) 3 4
-- Just 7
-- >>> runPartialSemigroupOp (addPositive <> total (+)) (-1) 4
-- Just 3
fallbackExample :: PartialSemigroupOp' Int
fallbackExample = addPositive <> total (+)

-- * Functor

-- | 'fmap' transforms the result.
--
-- >>> runPartialSemigroupOp (fmap (*10) addPositive) 3 4
-- Just 70
-- >>> runPartialSemigroupOp (fmap (*10) addPositive) (-1) 4
-- Nothing
fmapPartialExample :: PartialSemigroupOp' Int
fmapPartialExample = fmap (* 10) addPositive

-- * Using effects (monad transformer)

-- | A partial semigroup that tracks how many times it's been called,
-- returning 'Nothing' when either input is non-positive.
--
-- >>> runState (runPartialSemigroupOpT countedAdd 3 4) 0
-- (Just 7,1)
-- >>> runState (runPartialSemigroupOpT countedAdd (-1) 4) 0
-- (Nothing,1)
countedAdd :: PartialSemigroupOpT (State Int) Int Int
countedAdd =
  PartialSemigroupOpT
    ( \a b -> do
        n <- get
        put (n + 1)
        pure
          ( if a > 0 && b > 0
              then Just (a + b)
              else Nothing
          )
    )

-- helpers to suppress unused-import warnings for re-exports used in doctests
_suppressRunPartialSemigroupOp :: PartialSemigroupOp' a -> a -> a -> Maybe a
_suppressRunPartialSemigroupOp = runPartialSemigroupOp

_suppressRunPartialSemigroupOpT :: PartialSemigroupOpT f a b -> a -> a -> f (Maybe b)
_suppressRunPartialSemigroupOpT = runPartialSemigroupOpT