
Message: 8 Date: Sat, 5 Mar 2011 00:45:33 +0100 From: Yves Par?s
Subject: [Haskell-cafe] Use of uninstantiated type class To: Haskell-Cafe Message-ID: Content-Type: text/plain; charset="iso-8859-1" Hello,
For testing purposes, I am trying to make an overlay to IO which carries a phantom type to ensure a context. I define contexts using empty type classes :
class CtxFoo c class CtxBar c
The overlay :
newtype MyIO c a = MyIO (IO a)
Then I define some methods that run only a specific context :
runFoo :: (CtxFoo c) => MyIO c a -> IO a runFoo (MyIO x) = x
runBar :: (CtxBar c) => MyIO c a -> IO a runBar (MyIO x) = x
And then an action that runs in context 'Foo' :
someAction :: (CtxFoo c) => MyIO c () someAction = putStrLn "FOO"
Then I run it :
main = runFoo someAction
But obiously, GHC complains that my type 'c' remains uninstantiated :
Ambiguous type variable `c' in the constraint: (CtxFoo c) arising from a use of `runFoo' Probable fix: add a type signature that fixes these type variable(s) In the expression: runFoo someAction In an equation for `main': main = runFoo someAction
Is there a way to deal with this ? The interest of using type classes and not empty types to represent the contexts is that it stays simple, and that I can do that :
someAction2 :: (CtxFoo c, CtxBar c) => MyIO c () someAction2 = putStrLn "FOO and BAR"
... a function that can run in both contexts.
You can accomplish this with Rank2Types (and ScopedTypeVariables). Try this: class CtxFoo c class CtxBar c data Ctx instance CtxFoo Ctx where instance CtxBar Ctx where runFoo :: forall a. (forall c. (CtxFoo c) => MyIO c a) -> IO a runFoo x = case (x :: MyIO CtxFooD a) of (MyIO x') -> x' It's useful to compare the type of this "runFoo" with the old "runFoo" *Main> :t runFoo runFoo :: (forall c. CtxFoo c => MyIO c a) -> IO a *Main> :t runFooOld runBar :: CtxFoo c => MyIO c a -> IO a Note that the "c" type var is no longer universally quantified. This means that "runFoo" can't be used with any actions that specify a concrete context, the action must specify only the "CtxFoo" type class. There's a drawback to this approach though. The number of "run" functions is combinatorial in the number of contexts. With only two contexts, you already need "runFoo", "runBar", and "runFooBar". Unless you'll only ever need a single context, or you won't need to have combinations of them, this approach quickly grows unwieldy. John