[Haskell-cafe] Use of uninstantiated type class

John Lato jwlato at gmail.com
Sat Mar 5 13:59:25 CET 2011


> Message: 8
> Date: Sat, 5 Mar 2011 00:45:33 +0100
> From: Yves Par?s <limestrael at gmail.com>
> Subject: [Haskell-cafe] Use of uninstantiated type class
> To: Haskell-Cafe <haskell-cafe at haskell.org>
> Message-ID:
>        <AANLkTimBg5TyG6ZNWCKaOT9dP=QE95MtC=q_t7RRPQgv at mail.gmail.com>
> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20110305/ae26bd94/attachment.htm>


More information about the Haskell-Cafe mailing list