----------------------------------------------------------------------------- -- | -- Module : Language.Haskell.RoleAnnots -- Copyright : (C) 2014 Richard Eisenberg -- License : BSD-style (see LICENSE) -- Maintainer : Richard Eisenberg (eir@cis.upenn.edu) -- Stability : experimental -- Portability : non-portable -- -- This module provides three class constraints that can be used to make -- roles stricter in your datatypes. Here is a typical use case: -- -- > -- Use an association list as a map: -- > data (Nominal k, Representational v) => Map k v = MkMap [(k,v)] -- -- With a declaration such as this, GHC will assign correct roles to @k@ -- and @v@. In versions of GHC before roles, these constraints have no -- effect. You will need to make sure the language extension -- @-XDatatypeContexts@ is enabled. This extension is enabled by default -- with a default language of either Haskell98 or Haskell2010, but not -- by default with a vanilla invocation of GHC. When enabling it manually, -- you may also want to specify @-fno-warn-deprecated-flags@, as datatype -- contexts are generally a misfeature. -- -- Note that these constraints can only make roles stricter, such as a -- change from representational to nominal. Indeed, going the other way -- would not be type-safe! Thus, there is no guarantee that a parameter -- has the role given -- the guarantee is only that a parameter as -- /at least/ the role given. (Thus, 'Phantom' is always redundant and -- is included only for completeness.) -- -- To check that the roles are what you would -- expect, see module "Language.Haskell.RoleAnnots.Check". -- ---------------------------------------------------------------------------- {-# LANGUAGE CPP, PolyKinds, FlexibleInstances, Trustworthy #-} #if __GLASGOW_HASKELL__ < 707 {-# LANGUAGE DeriveDataTypeable #-} #else {-# LANGUAGE IncoherentInstances, RoleAnnotations #-} #endif module Language.Haskell.RoleAnnots ( Nominal, Representational, Phantom, roleAnnot, Role(..) ) where import Language.Haskell.TH #if __GLASGOW_HASKELL__ < 707 import Data.Data ( Data, Typeable ) #endif -- | Mark a type parameter as having a nominal role class Nominal a -- | Mark a type parameter as having a representational role class Representational a -- | Mark a type parameter as having a phantom role. (This is always redundant.) class Phantom a instance Nominal a instance Representational a instance Phantom a #if __GLASGOW_HASKELL__ >= 707 type role Nominal nominal -- this annotation is redundant type role Representational representational type role Phantom phantom #endif {-# DEPRECATED roleAnnot "The `roleAnnot` function is not necessary. Use a role constraint in a datatype context instead." #-} -- | Deprecated since role inference looks at datatype contexts (with the release -- of GHC 7.8). roleAnnot :: [Role] -> Q [Dec] -> Q [Dec] roleAnnot _roles qdecs = do #if __GLASGOW_HASKELL__ < 707 qdecs #else decs <- qdecs case decs of [DataD _ name _ _ _] -> return $ RoleAnnotD name _roles : decs [NewtypeD _ name _ _ _] -> return $ RoleAnnotD name _roles : decs _ -> (reportError $ "Please pass exactly one \"data\" " ++ "or \"newtype\" declaration to " ++ "roleAnnot.") >> return decs #endif #if __GLASGOW_HASKELL__ < 707 -- | This declaration mirrors the declaration within Template Haskell, for -- use in earlier versions of GHC. data Role = NominalR | RepresentationalR | PhantomR | InferR deriving (Show, Eq, Data, Typeable) #endif