{-# LANGUAGE UndecidableInstances #-}
module Wakame.Union where

import Prelude

import Control.Arrow (first)
import Wakame.Row (NP (..), Row)
import Wakame.Utils (type (++))


-- $setup
-- >>> import GHC.Generics
-- >>> import Wakame
-- >>> data Point = Point { x :: Double, y :: Double } deriving (Show, Generic)
-- >>> pt = Point 1.2 8.3


-- | Typeclass for composing fields
--
-- >>> union (toRow pt) (toRow (keyed @"z" 42.0))
-- (x: 1.2) :* (y: 8.3) :* (z: 42.0) :* Nil
--
-- >>> ununion (toRow pt) :: (Row '[ '("x", Double)], Row '[ '("y", Double)])
-- ((x: 1.2) :* Nil,(y: 8.3) :* Nil)
-- >>> ununion (toRow pt) :: (Row '[], Row '[ '("x", Double), '("y", Double)])
-- (Nil,(x: 1.2) :* (y: 8.3) :* Nil)
class (u ~ (l ++ r)) => Union l r u | l r -> u where
  union :: Row l -> Row r -> Row u
  ununion :: Row u -> (Row l, Row r)

instance Union '[] r r where
  union _ r = r
  ununion x = (Nil, x)

instance Union l r u => Union (x ': l) r (x ': u) where
  union (x :* xs) r = x :* union xs r
  ununion (x :* xs) = first (x :*) $ ununion xs