Knowing that:
a has the form K- the only types of type
K are a and B Show (Proxy A) containsShow (Proxy B) contains
enough to prove that Show (Proxy A) satisfied. But a type class is not just a proposition that we need to prove for use with our type, it also provides an implementation. In order to actually use show (x :: Proxy a) , we need not only to prove that there is an implementation for Show (Proxy A) , we really need to know which one is there.
Variables of the Haskell type (without restrictions) are parametric: there is no way to be completely polymorphic in a , and also to be able to test a to provide different behavior for a and B The “different behavior” you want is roughly “close to parametric,” since you may not be parametric, as it is just choosing a different instance for each type, when you know that there is one for each type. But this is still something that violates the contract, which means test :: forall (a :: K). Proxy a -> String test :: forall (a :: K). Proxy a -> String .
Class type restrictions allow your code to be nonparametric in variables with a limited type, since you can use class type mapping from types to implementations to switch the behavior of your code to the type that it refers to. So test :: forall (a :: K). Show (Proxy a) => Proxy a -> String test :: forall (a :: K). Show (Proxy a) => Proxy a -> String works. (In terms of the actual implementation, the same end-user who selects type a also provides an instance of Show (Proxy A) for the code generated by your function in use.)
You can use the idea of instance Show (Proxy (a :: K)) ; now your function, which is parametric in type a :: K , can still use show (x :: Proxy a) , because there is one instance that works for all a :: K But the instance itself is facing the same problem; the show implementation in the instance is parametric in a , and therefore cannot check it anyway to return another type-based string.
You can use something like dfeuer's answer, where Kish uses the variable parameter type with a limited type to basically let you check the type at runtime; the instance dictionary passed to satisfy the Kish a constraint is basically a run-time record for which the type was chosen for the variable a (in a circular fashion). Tapping this idea will complement you to Typeable . But you still need some kind of restriction to make your code nonparametric in a .
You can also use a type that explicitly represents the timing of a or B (unlike Proxy ), which is an empty value at runtime and provides only a representation of the compilation time of the choice), something like:
{-
(Please note that I can not only implement Show (Kproxy a) , but also get it, although this requires standalone output)
This is the use of GADT KProxy to allow test be nonparametric in a , essentially doing the same job as the Kish constraint from dfeuer's answer, but avoiding the need to add an extra constraint to sign your type. In an earlier version of this post, I stated that test was able to do this while remaining “fair” parametric in a , which was incorrect.
Of course, now, in order to transfer the proxy server, you must write KA or KB . Don’t worry where you had to write Proxy :: Proxy A to actually choose the type (which is often the case with proxies, given that you usually use them only to fix the type, which would otherwise be ambiguous). But where it would be unambiguous, the compiler will catch you if you do not agree with the rest of the call, but you cannot write only one character, such as Proxy , and the compiler will output the correct value. You can refer to this class of types:
class KProxyable (a :: K) where kproxy :: KProxy a instance KProxyable A where kproxy = KA instance KProxyable B where kproxy = KB
Then you can use KA instead of Proxy :: Proxy A and KProxy instead of having the compiler KProxy bare Proxy type. Stupid approximate time:
foo :: KProxy a -> KProxy a -> String foo kx ky = show kx ++ " " ++ show ky
GHCI:
λ foo KA kproxy "KA KA"
Note. I do not need to have a KProxyable restriction anywhere; I use KProxy at the point where the type is known. It should still “come in from above,” though (just like an instance dictionary that satisfies your Show (Proxy A) constraint); it is not possible to have a function parametric in type a :: K , to independently use the corresponding KProxy a .
Since this is a correspondence between the constructors and the type that does this work, I do not believe that you can make a general proxy in this style the way you can with an empty Proxy runtime. However, TemplateHaskell can generate these types of proxies for you; I think the singleton concept is a general idea here, and therefore https://hackage.haskell.org/package/singletons probably provides what you need, but I'm not very familiar with how to really use this package.