{-| Copyright : (C) 2018 , QBayLogic B.V. License : BSD2 (see the file LICENSE) Maintainer : Christiaan Baaij <christiaan.baaij@gmail.com> Hidden arguments -} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE Trustworthy #-} module Clash.Hidden ( Hidden , expose -- * OverloadedLabels , fromLabel ) where import qualified GHC.Classes import GHC.TypeLits import Unsafe.Coerce -- | A value reflected to, or /hiding/ at, the /Constraint/ level -- -- e.g. a function: -- -- @ -- f :: Hidden "foo" Int -- => Bool -- -> Int -- f = ... -- @ -- -- has a /normal/ argument of type @Bool@, and a /hidden/ argument called \"foo\" -- of type @Int@. In order to apply the @Int@ argument we have to use the -- 'expose' function, so that the /hidden/ argument becomes a normal argument -- again. -- -- === __Original implementation__ -- -- 'Hidden' used to be implemented by: -- -- @ -- class Hidden (x :: Symbol) a | x -> a where -- hidden :: a -- @ -- -- which is equivalent to /IP/, except that /IP/ has magic inference rules -- bestowed by GHC so that there's never any ambiguity. We need these magic -- inference rules so we don't end up in type inference absurdity where asking -- for the type of an type-annotated value results in a /no-instance-in-scope/ -- error. type Hidden (x :: Symbol) a = GHC.Classes.IP x a newtype Secret x a r = Secret (Hidden x a => r) -- | Expose a 'Hidden' argument so that it can be applied normally, e.g. -- -- @ -- f :: Hidden "foo" Int -- => Bool -- -> Int -- f = ... -- -- g :: Int -> Bool -> Int -- g = 'expose' \@\"foo" f -- @ expose :: forall x a r . (Hidden x a => r) -- ^ Function with a 'Hidden' argument -> (a -> r) -- ^ Function with the 'Hidden' argument exposed expose :: (Hidden x a => r) -> a -> r expose Hidden x a => r k = Secret x a r -> a -> r forall a b. a -> b unsafeCoerce ((Hidden x a => r) -> Secret x a r forall (x :: Symbol) a r. (Hidden x a => r) -> Secret x a r Secret @x @a @r Hidden x a => r k) {-# INLINE expose #-} -- | Using /-XOverloadedLabels/ and /-XRebindableSyntax/, we can turn any -- value into a /hidden/ argument using the @#foo@ notation, e.g.: -- -- @ -- f :: Int -> Bool -> Int -- f = ... -- -- g :: Hidden "foo" Bool -- => Int -> Int -- g i = f i #foo -- @ fromLabel :: forall x a . Hidden x a => a fromLabel :: a fromLabel = forall a. IP x a => a forall (x :: Symbol) a. IP x a => a GHC.Classes.ip @x {-# INLINE fromLabel #-}