packages feed

type-rig-0.1: lib/Data/TypeRig/Riggable.hs

module Data.TypeRig.Riggable where

import Control.Arrow
import Data.Either
import Data.Functor
import Data.Functor.Invariant
import Data.Kind
import Data.List.NonEmpty
import Data.Maybe
import Data.Semigroup
import Data.TypeRig.Productable
import Data.TypeRig.Summable
import Prelude hiding ((.), id)
import qualified Text.ParserCombinators.ReadP as ReadP
import qualified Text.ParserCombinators.ReadPrec as ReadPrec

-- | Composability via a [rig](https://ncatlab.org/nlab/show/rig) of types.
type Riggable :: (Type -> Type) -> Constraint
class (Productable f, Summable f) => Riggable f where
    rOptional :: forall a. f a -> f (Maybe a)
    rOptional fa = let
        eitherToMaybe :: Either a () -> Maybe a
        eitherToMaybe (Left a) = Just a
        eitherToMaybe (Right ()) = Nothing
        maybeToEither :: Maybe a -> Either a ()
        maybeToEither (Just a) = Left a
        maybeToEither Nothing = Right ()
        in invmap eitherToMaybe maybeToEither $ fa <+++> rUnit
    rList1 :: f a -> f (NonEmpty a)
    rList1 fa = let
        pairToNonEmpty :: (a, [a]) -> NonEmpty a
        pairToNonEmpty (a, as) = a :| as
        nonEmptyToPair :: NonEmpty a -> (a, [a])
        nonEmptyToPair (a :| as) = (a, as)
        in invmap pairToNonEmpty nonEmptyToPair $ fa <***> rList fa
    rList :: f a -> f [a]
    rList fa = let
        eitherToList :: Either (NonEmpty a) () -> [a]
        eitherToList (Left (a :| aa)) = a : aa
        eitherToList (Right ()) = []
        listToEither :: [a] -> Either (NonEmpty a) ()
        listToEither (a:aa) = Left $ a :| aa
        listToEither [] = Right ()
        in invmap eitherToList listToEither $ rList1 fa <+++> rUnit

instance Riggable Endo where
    rOptional (Endo f) = Endo $ fmap f
    rList1 (Endo f) = Endo $ fmap f
    rList (Endo f) = Endo $ fmap f

instance Riggable m => Riggable (Kleisli m a) where
    rOptional (Kleisli f) = Kleisli $ \a -> rOptional $ f a
    rList1 (Kleisli f) = Kleisli $ \a -> rList1 $ f a
    rList (Kleisli f) = Kleisli $ \a -> rList $ f a

instance Riggable ReadPrec.ReadPrec where
    rOptional ra = ReadPrec.readP_to_Prec $ \prec -> ReadP.option Nothing $ fmap Just $ ReadPrec.readPrec_to_P ra prec
    rList ra = ReadPrec.readP_to_Prec $ \prec -> ReadP.many $ ReadPrec.readPrec_to_P ra prec