{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Servant.API.UVerb.Union
( IsMember
, Unique
, Union
, inject
, eject
, foldMapUnion
, matchUnion
)
where
import Data.Proxy (Proxy)
import Data.SOP.BasicFunctors (I, unI)
import Data.SOP.Constraint
import Data.SOP.NS
import Data.Type.Bool (If)
import Data.Type.Equality (type (==))
import GHC.TypeLits
type Union = NS I
foldMapUnion ::
forall (c :: * -> Constraint) (a :: *) (as :: [*]).
All c as =>
Proxy c ->
(forall x. c x => x -> a) ->
Union as ->
a
foldMapUnion :: Proxy c -> (forall x. c x => x -> a) -> Union as -> a
foldMapUnion Proxy c
proxy forall x. c x => x -> a
go = Proxy c -> (forall a. c a => I a -> a) -> Union as -> a
forall k (c :: k -> Constraint) (proxy :: (k -> Constraint) -> *)
(f :: k -> *) (xs :: [k]) m.
All c xs =>
proxy c -> (forall (a :: k). c a => f a -> m) -> NS f xs -> m
cfoldMap_NS Proxy c
proxy (a -> a
forall x. c x => x -> a
go (a -> a) -> (I a -> a) -> I a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. I a -> a
forall a. I a -> a
unI)
matchUnion :: forall (a :: *) (as :: [*]). (IsMember a as) => Union as -> Maybe a
matchUnion :: Union as -> Maybe a
matchUnion = (I a -> a) -> Maybe (I a) -> Maybe a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap I a -> a
forall a. I a -> a
unI (Maybe (I a) -> Maybe a)
-> (Union as -> Maybe (I a)) -> Union as -> Maybe a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Union as -> Maybe (I a)
forall k (x :: k) (xs :: [k]) (f :: k -> *).
UElem x xs =>
NS f xs -> Maybe (f x)
eject
type IsMember (a :: u) (as :: [u]) = (Unique as, CheckElemIsMember a as, UElem a as)
class UElem x xs where
inject :: f x -> NS f xs
eject :: NS f xs -> Maybe (f x)
instance {-# OVERLAPPING #-} UElem x (x ': xs) where
inject :: f x -> NS f (x : xs)
inject = f x -> NS f (x : xs)
forall k (a :: k -> *) (x :: k) (xs :: [k]). a x -> NS a (x : xs)
Z
eject :: NS f (x : xs) -> Maybe (f x)
eject (Z f x
x) = f x -> Maybe (f x)
forall a. a -> Maybe a
Just f x
x
eject NS f (x : xs)
_ = Maybe (f x)
forall a. Maybe a
Nothing
instance {-# OVERLAPPING #-} UElem x xs => UElem x (x' ': xs) where
inject :: f x -> NS f (x' : xs)
inject = NS f xs -> NS f (x' : xs)
forall k (a :: k -> *) (xs :: [k]) (x :: k).
NS a xs -> NS a (x : xs)
S (NS f xs -> NS f (x' : xs))
-> (f x -> NS f xs) -> f x -> NS f (x' : xs)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. f x -> NS f xs
forall k (x :: k) (xs :: [k]) (f :: k -> *).
UElem x xs =>
f x -> NS f xs
inject
eject :: NS f (x' : xs) -> Maybe (f x)
eject (Z f x
_) = Maybe (f x)
forall a. Maybe a
Nothing
eject (S NS f xs
ns) = NS f xs -> Maybe (f x)
forall k (x :: k) (xs :: [k]) (f :: k -> *).
UElem x xs =>
NS f xs -> Maybe (f x)
eject NS f xs
ns
type family CheckElemIsMember (a :: k) (as :: [k]) :: Constraint where
CheckElemIsMember a as =
If (Elem a as) (() :: Constraint) (TypeError (NoElementError a as))
type NoElementError (r :: k) (rs :: [k]) =
'Text "Expected one of:"
':$$: 'Text " " ':<>: 'ShowType rs
':$$: 'Text "But got:"
':$$: 'Text " " ':<>: 'ShowType r
type DuplicateElementError (rs :: [k]) =
'Text "Duplicate element in list:"
':$$: 'Text " " ':<>: 'ShowType rs
type family Elem (x :: k) (xs :: [k]) :: Bool where
Elem _ '[] = 'False
Elem x (x' ': xs) =
If (x == x') 'True (Elem x xs)
type family Unique xs :: Constraint where
Unique xs = If (Nubbed xs == 'True) (() :: Constraint) (TypeError (DuplicateElementError xs))
type family Nubbed xs :: Bool where
Nubbed '[] = 'True
Nubbed (x ': xs) = If (Elem x xs) 'False (Nubbed xs)
_testNubbed :: ( ( Nubbed '[Bool, Int, Int] ~ 'False
, Nubbed '[Int, Int, Bool] ~ 'False
, Nubbed '[Int, Bool] ~ 'True
)
=> a) -> a
_testNubbed :: ((Nubbed '[Bool, Int, Int] ~ 'False,
Nubbed '[Int, Int, Bool] ~ 'False, Nubbed '[Int, Bool] ~ 'True) =>
a)
-> a
_testNubbed = ((Nubbed '[Bool, Int, Int] ~ 'False,
Nubbed '[Int, Int, Bool] ~ 'False, Nubbed '[Int, Bool] ~ 'True) =>
a)
-> a
forall a. a -> a
id