I want to write a typeclass for types that have a static pointer to a dictionary that can discharge the super class constraints. In this contrived example the super-constraint is Typeable
, since modern GHC versions automatically provides the instance for all types.
In reality, the super-constraints are non-trivial and instances of Static a
allow the dictionaries of those constraints to be serialized, since forall a. StaticPtr a
can be serialized.
First, the following code compiles without issue,
{-# LANGUAGE StaticPointers #-}
{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}
{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
{-# LANGUAGE UndecideableInstances, MultiParamTypeClasses #-}
import GHC.StaticPtr
import Data.Constraint
import Data.Constraint.Lifting
import Data.Typeable (Typeable)
class Typeable a => Static a where
sdict :: Dict (Static a)
-- The 'Typeable f' constraint should be trivial, but typechecking
-- fails without it. Is it possible to get rid of it, if not then why?
instance (Typeable f, Lifting Static f, Static a) => Static (f a) where
sdict = Dict \\ lifting @Static @f @a
But when I write the class in the desired way using static pointers,
class Typeable a => Static a where
sdict :: StaticPtr (Dict (Static a))
instance (Typeable f, Lifting Static f, Static a) => Static (f a) where
sdict = static (Dict \\ lifting @Static @f @a)
GHC complains about type applications in the body of a static form:
Only identifiers of top-level bindings can appear in the body of the static form:
static (Dict \\ lifting @Static @f @a)
but the following identifiers were found instead: f a
Then I tried to let GHC resolve the instances entirely on its own, without using Lifting
to guide the typechecker
instance (Typeable f, Static a) => Static (f a) where sdict = static Dict
But this fails as well,
• No instance for (Static a) arising from a use of ‘Dict’
• In the body of a static form: Dict
In the expression: static Dict
In an equation for ‘sdict’: sdict = static Dict
A more monomorphic instance also fails with a very similar error: No instance for (Static a)
,
instance Static a => Static [a] where sdict = static Dict
This led me to learn more about how GHC's typeclass instance resolution works: a class's super-constraints are not applied until after the instance is actually resolved. But GHC was able to satisfy Dict :: Static a => Dict (Static a)
in the first example, which clearly requires a Static a
instance to be in scope.
Any suggestions how to write this, if it's even possible? Hopefully someone with more experience with the constraints package can provide the right contortions to satisfy the typechecker.