packages feed

type-set-0.1.0.0: src/Data/OneOfThem.hs

{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE DataKinds, TypeOperators #-}
{-# LANGUAGE GADTs, TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Data.OneOfThem (
	-- * OneOfThem
	-- ** Type
	OneOfThem,
	-- ** Single
	pattern Singleton, unSingleton,
	-- ** Multiple
	-- *** Project
	Projectable, project,
	-- *** Expand
	Expandable, (>-), expand,
	-- * OneOfThemFun
	-- ** Type and Apply
	OneOfThemFun, apply,
	-- ** Single
	pattern SingletonFun,
	-- ** Insert
	InsertableFun, (>--),
	-- ** Merge
	MergeableFun, mergeFun
	) where

import Data.Kind (Type)
import Data.Type.Set.Internal -- (Set(Nil, (:~)), Singleton)

data OneOfThem :: Set Type -> Type where
	JustIt :: a -> OneOfThem (a ':~ as)
	Wrap :: OneOfThem as -> OneOfThem (a ':~ as)

instance Show (OneOfThem 'Nil) where
	show _ = error "bad"

instance (Show a, Show (OneOfThem as)) => Show (OneOfThem (a :~ as)) where
	show (JustIt x) = "(JustIt " ++ show x ++ ")"
	show (Wrap xs) = show xs

{-# COMPLETE Singleton #-}

pattern Singleton :: a -> OneOfThem (Singleton a)
pattern Singleton x = JustIt x

unSingleton :: OneOfThem (Singleton a) -> a
unSingleton (Singleton x) = x

class Expandable (as :: Set Type) (as' :: Set Type) where
	expand :: OneOfThem as -> OneOfThem as'

instance Expandable 'Nil as where expand _ = error "never occur"

instance Expandable as as' => Expandable (a ':~ as) (a ':~ as') where
	expand (JustIt x) = JustIt x
	expand (Wrap oot) = Wrap $ expand oot

instance {-# OVERLAPPABLE #-} Expandable (a ':~ as) as' =>
	Expandable (a ':~ as) (a' ':~ as') where
	expand x = Wrap $ expand x

class Projectable (as :: Set Type) a where project :: OneOfThem as -> Maybe a

instance Projectable 'Nil a where project _ = Nothing
instance Projectable (a ':~ as) a where
	project (JustIt x) = Just x
	project (Wrap _) = Nothing
instance {-# OVERLAPPABLE #-} Projectable as a =>
	Projectable (a' ':~ as) a where
	project (JustIt _) = Nothing
	project (Wrap xs) = project xs

class Collapsable (as :: Set Type) (as' :: Set Type) where
	collapse :: OneOfThem as -> Maybe (OneOfThem as')

instance Collapsable as 'Nil where collapse _ = Nothing

instance Collapsable as as' => Collapsable (a ':~ as) (a ':~ as') where
	collapse (JustIt x) = Just $ JustIt x
	collapse (Wrap oot) = Wrap <$> collapse oot

instance {-# OVERLAPPABLE #-} Collapsable as (a' ':~ as') =>
	Collapsable (a ':~ as) (a' ':~ as') where
	collapse (JustIt _) = Nothing
	collapse (Wrap oot) = collapse oot

data OneOfThemFun (as :: Set Type) b where
	EmptyFun :: OneOfThemFun 'Nil b
	(:..) :: (a -> b) -> OneOfThemFun as b -> OneOfThemFun (a ':~ as) b

class InsertableFun a (as :: Set Type) (as' :: Set Type) where
	(>--.) :: (a -> b) -> OneOfThemFun as b -> OneOfThemFun as' b

instance InsertableFun a as (a ':~ as) where f >--. fs = f :.. fs

instance {-# OVERLAPPABLE #-} InsertableFun a as as' =>
	InsertableFun a (a' ':~ as) (a' ':~ as') where
	f >--. (g :.. fs) = g :.. (f >--. fs)

infixr 5 >-, >--

(>--) :: InsertableFun a as (a :- as) => (a -> b) -> OneOfThemFun as b -> OneOfThemFun (a :- as) b
(>--) = (>--.)

{-# COMPLETE SingletonFun #-}

pattern SingletonFun :: (a -> b) -> OneOfThemFun (Singleton a) b
pattern SingletonFun f = f :.. EmptyFun

class Applyable as where
	apply :: OneOfThemFun as b -> OneOfThem as -> b

instance Applyable (Singleton a) where
	apply (SingletonFun f) (Singleton x) = f x

instance {-# OVERLAPPABLE #-} Applyable as => Applyable (a ':~ as) where
	apply (f :.. _) (JustIt x) = f x
	apply (_ :.. fs) (Wrap xs) = fs `apply` xs

(>-) :: (Expandable (Singleton a) (a :- as), Expandable as (a :- as)) => a -> [OneOfThem as] -> [OneOfThem (a :- as)]
x >- xs = expand (Singleton x) : (expand <$> xs)

class MergeableFun as as' mrg where
	mergeFun_ :: OneOfThemFun as b -> OneOfThemFun as' b -> OneOfThemFun mrg b

instance MergeableFun 'Nil 'Nil 'Nil where
	mergeFun_ EmptyFun EmptyFun = EmptyFun

instance MergeableFun as as' mrg => MergeableFun as (a' ':~ as') (a' ':~ mrg) where
	mergeFun_ fs (g :.. gs) = g :.. mergeFun_ fs gs

instance MergeableFun as as' mrg => MergeableFun (a ':~ as) as' (a ':~ mrg) where
	mergeFun_ (f :.. fs) gs = f :.. mergeFun_ fs gs

mergeFun :: MergeableFun as as' (as :+: as') =>
	OneOfThemFun as b -> OneOfThemFun as' b -> OneOfThemFun (as :+: as') b
mergeFun = mergeFun_