{-# LANGUAGE FlexibleInstances, PolyKinds, RankNTypes  #-}
{-# LANGUAGE ScopedTypeVariables, UndecidableInstances #-}
-- | Really unsafe module to provide internal interface.
-- This module should be imported if you wish to allow the unsafe computation globally.
-- But be careful: the unsafety contaminates entire the program once you write
-- the instance for 'ReallyUnsafe' somewhere.
module Data.Constraint.Unsafely.Really (
  ReallyUnsafely, Unsafely, unsafely
  ) where
import Unsafe.Coerce

-- | The trick type-class to prevent providing global instances for @Unsafely@.
-- This class is not exported in "Data.Constraint.Unsafely", so if you want to
-- permit unsafe operations globally, you should import this module directly.
class ReallyUnsafely t

-- | The constraint for the instances which might be /unsafe/ in some sence.
-- @t@ in @Unsafely t@ is the dummy tag for some series of /unsafe/ operations.
class ReallyUnsafely t => Unsafely (t :: k) where
  impossible :: Impossible t
instance ReallyUnsafely t => Unsafely t where
  impossible = Impossible
-- | Witness to allow /unsafe/ computation.
-- This type never be exported, which ensures that you can call @unsafely@ safely.
data Impossible (t :: k) = Impossible

newtype Thingy t a = Thingy (Unsafely t => a)

-- | Evaluate the value which might be unsafe.
unsafely :: forall proxy t a. proxy t -> (Unsafely t => a) -> a
unsafely _ f = unsafeCoerce (Thingy f :: Thingy t a) Impossible