{-# LANGUAGE
    FlexibleInstances
  , UndecidableInstances
  #-}

module Data.Mergeable where

import Data.Commutative
import Data.List.NonEmpty as NE
import qualified Data.Vector as Vector


class Mergeable t where
  mergeMap :: CommutativeId m => (a -> m) -> t a -> m
  merge :: (a -> b -> b) -> b -> t a -> b

  mergeMap f = merge (commute . f) cempty
  merge f i xs = appCommEndo (mergeMap (CommEndo . f) xs) i

instance Mergeable [] where
  mergeMap _ [] = cempty
  mergeMap f (x:xs) = f x <~> mergeMap f xs

instance Mergeable Vector.Vector where
  mergeMap f xss
    | Vector.null xss = cempty
    | otherwise = f (Vector.head xss) <~> mergeMap f (Vector.drop 1 xss)


class Functor t => Mergeable1 t where
  mergeMap1 :: Commutative m => (a -> m) -> t a -> m
  merge1 :: Commutative m => t m -> m

  mergeMap1 f = merge1 . fmap f
  merge1 = mergeMap1 id

instance Mergeable1 NonEmpty where
  mergeMap1 f (x:|[]) = f x
  mergeMap1 f (x:|xs) = f x <~> mergeMap1 f (NE.fromList xs)