module Data.Semigroup (Monoid(..), Endo, Fusion, Point(..), Semigroup(..), endoMaybe, endoPoint, fusing', fusing, fuseCat, scatPoints', scatPoints, pJust, pNothing) where
import Data.Monoid(Monoid(..))
import Data.Maybe
import GHC.Exts
type Endo a = a -> a -> a
type Fusion a = [a] -> Maybe a
newtype Point a = Pt (Maybe a) deriving (Functor, Eq, Show, Read)
class Semigroup a where
sappend :: Endo a
sconcat :: Fusion a
sconcat = fusing
sconcat_ :: a -> [a] -> a
sconcat_ x xs = fromJust (sconcat (x:xs))
instance Monoid a => Semigroup a where
sappend = mappend
sconcat = Just . mconcat
sconcat_ x xs = mconcat (x:xs)
instance Semigroup a => Monoid (Point a) where
mempty = Pt Nothing
mappend = endoPoint sappend
mconcat = Pt . scatPoints
endoMaybe :: Endo a -> Endo (Maybe a)
endoMaybe (><) (Just a) = Just . maybe a (a ><)
endoMaybe _ Nothing = id
endoPoint :: Endo a -> Endo (Point a)
endoPoint (><) (Pt a) (Pt b) = Pt (endoMaybe (><) a b)
fusing' :: Endo a -> Fusion a
fusing' _ [] = Nothing
fusing' (><) (q:qs) = Just $ fuseCat (><) q qs
fuseCat :: Endo a -> a -> [a] -> a
fuseCat (><) = fuseCat' where
fuseCat' x [] = x
fuseCat' x xs = case fuse1 x xs of ( x', xs' ) -> fuseCat' x' xs'
fuse1 x1 (x2:x3:x4:xs) = ( (x1 >< x2) >< (x3 >< x4), fuser xs )
fuse1 x1 [x2,x3] = ( x1 >< x2 >< x3, [] )
fuse1 x1 [x2] = ( x1 >< x2, [] )
fuse1 x1 [] = ( x1, [] )
fuser [] = []
fuser (x:xs) = case fuse1 x xs of ( x, xs ) -> x:xs
fusing :: Semigroup a => Fusion a
fusing = fusing' sappend
scatPoints' :: Fusion a -> [Point a] -> Maybe a
scatPoints' cat xs = cat [x | Pt (Just x) <- xs]
scatPoints :: Semigroup a => [Point a] -> Maybe a
scatPoints = scatPoints' sconcat
pJust :: a -> Point a
pJust = Pt . Just
pNothing :: Point a
pNothing = Pt Nothing