{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveDataTypeable #-}
module Data.Constraint.Deferrable
( UnsatisfiedConstraint(..)
, Deferrable(..)
, defer
, deferred
) where
import Control.Exception
import Control.Monad
import Data.Constraint
import Data.Proxy
import Data.Typeable (Typeable, cast)
data UnsatisfiedConstraint = UnsatisfiedConstraint String
deriving (Typeable, Show)
instance Exception UnsatisfiedConstraint
class Deferrable (p :: Constraint) where
deferEither :: proxy p -> (p => r) -> Either String r
defer :: forall proxy p r. Deferrable p => proxy p -> (p => r) -> r
defer _ r = either (throw . UnsatisfiedConstraint) id $ deferEither (Proxy :: Proxy p) r
deferred :: forall p. Deferrable p :- p
deferred = Sub $ defer (Proxy :: Proxy p) Dict
data a :~: b where
Refl :: a :~: a
deriving Typeable
instance (Typeable a, Typeable b) => Deferrable (a ~ b) where
deferEither _ r = case cast (Refl :: a :~: a) :: Maybe (a :~: b) of
Just Refl -> Right r
Nothing -> Left "deferred type equality: type mismatch"
instance (Deferrable a, Deferrable b) => Deferrable (a, b) where
deferEither _ r = join $ deferEither (Proxy :: Proxy a) $ deferEither (Proxy :: Proxy b) r
instance (Deferrable a, Deferrable b, Deferrable c) => Deferrable (a, b, c) where
deferEither _ r = join $ deferEither (Proxy :: Proxy a) $ join $ deferEither (Proxy :: Proxy b) $ deferEither (Proxy :: Proxy c) r