yaya-0.4.1.0: src/Yaya/Fold/Common.hs
-- | Common algebras that are useful when folding.
module Yaya.Fold.Common where
import Control.Monad
import Control.Monad.Trans.Free
import Data.Foldable
import Data.Functor.Classes
import Data.Functor.Day
import Data.Functor.Identity
import Numeric.Natural
import Yaya.Pattern
-- | Converts the free monoid (a list) into some other `Monoid`.
lowerMonoid :: Monoid m => (a -> m) -> XNor a m -> m
lowerMonoid f = \case
Neither -> mempty
Both a b -> mappend (f a) b
-- | Converts the free semigroup (a non-empty list) into some other `Semigroup`.
lowerSemigroup :: Semigroup m => (a -> m) -> AndMaybe a m -> m
lowerSemigroup f = \case
Only a -> f a
Indeed a b -> f a <> b
-- | Converts the free monad into some other `Monad`.
lowerMonad :: Monad m => (forall x. f x -> m x) -> FreeF f a (m a) -> m a
lowerMonad f = \case
Pure a -> pure a
Free fm -> join (f fm)
-- | Provides equality over arbitrary pattern functors.
equal :: (Functor f, Foldable f, Eq1 f) => Day f f Bool -> Bool
equal (Day f1 f2 fn) =
liftEq (==) (void f1) (void f2)
&& and (zipWith fn (toList f1) (toList f2))
-- TODO: Redefine this using `Natural`
-- | When folded, returns the height of the data structure.
height :: Foldable f => f Integer -> Integer
height = (+ 1) . foldr max (-1)
-- NB: It seems like this could be some more general notion of this, like
-- size :: (Foldable f, Semiring a) => f a -> a
-- size = foldr (+) one
-- | When folded, returns the number of nodes in the data structure.
--
-- __NB__: This is /not/ the same as the length when applied to a list. I.e.,
-- @`length` xs + 1 == `cata` `size` xs@, because this is counting the
-- nodes of the structure (how many `Neither`s and `Both`s), not how
-- many elements (which would be equivalent to only counting `Both`s).
size :: Foldable f => f Natural -> Natural
size = foldr (+) 1
-- | Converts a provably infinite structure into a `Yaya.Zoo.Partial` one (that
-- will never terminate).
toRight :: Identity b -> Either a b
toRight = Right . runIdentity
-- | Captures the input value if the application was undefined.
definedOrInput :: (a -> Maybe b) -> a -> Either a b
definedOrInput f a = maybe (Left a) Right $ f a
-- | Collapses a `Yaya.Zoo.Partial` structure to a value (probably requiring
-- unsafe instances).
fromEither :: Either a a -> a
fromEither = either id id
-- | Generates an infinite structure from an arbitrary seed.
never :: a -> Identity a
never = Identity
le :: Day Maybe Maybe Bool -> Bool
le = \case
Day Nothing _ _ -> True
Day (Just a) (Just b) f -> f a b
Day (Just _) Nothing _ -> False
takeAnother :: Day Maybe ((,) a) b -> XNor a b
takeAnother = \case
Day Nothing _ _ -> Neither
Day (Just x) (h, t) f -> Both h (f x t)
takeAvailable :: Day Maybe (XNor a) b -> XNor a b
takeAvailable = \case
Day Nothing _ _ -> Neither
Day (Just x) t f -> fmap (f x) t
takeNext :: Day Maybe ((,) a) a -> a
takeNext = \case
Day Nothing (h, _) _ -> h
Day (Just x) (_, t) f -> f x t
maybeTakeNext :: Day Maybe (XNor a) (Maybe a) -> Maybe a
maybeTakeNext = \case
Day Nothing (Both h _) _ -> Just h
Day (Just x) (Both _ t) f -> f x t
Day _ Neither _ -> Nothing
truncate' :: Functor f => Day Maybe f a -> FreeF f () a
truncate' = \case
Day Nothing _ _ -> Pure ()
Day (Just n) fa f -> Free (fmap (f n) fa)
-- | Converts a single value into a tuple with the same value on both sides.
-- > x &&& y = (x *** y) . diagonal
diagonal :: a -> (a, a)
diagonal x = (x, x)
-- * sequence generators
--
-- These functions are defined with different type parameters in order to
-- constrain the implementation, but to be used as coalgebras, all of the
-- parameters need to be specialized to the same type.
unarySequence :: (a -> b) -> a -> (a, b)
unarySequence f a = (a, f a)
binarySequence :: (a -> b -> c) -> (a, b) -> (a, (b, c))
binarySequence f (a, b) = (a, (b, f a b))
ternarySequence :: (a -> b -> c -> d) -> (a, b, c) -> (a, (b, c, d))
ternarySequence f (a, b, c) = (a, (b, c, f a b c))
lucasSequence' :: Integral i => i -> i -> (i, i) -> (i, (i, i))
lucasSequence' p q = binarySequence (\n2 n1 -> p * n1 - q * n2)