I am trying to implement a "DrawEnv" type class indexed by a point type:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}
class Monad m => DrawEnv p m where
box :: p -> p -> m ()
clear :: m ()
line :: p -> p -> m ()
type Pos = (Float,Float)
instance DrawEnv Pos IO where
box p0 p1 = putStrLn $ "Box " ++ show p0 ++ " " ++ show p1
clear = putStrLn "Clear"
line p0 p1 = putStrLn $ "Line " ++ show p0 ++ " " ++ show p1
draw :: DrawEnv Pos m => m ()
draw = do
clear
box (10.0,10.0) (100.0,100.0)
line (10.0,10.0) (100.0,50.0)
However GHC isn't happy:
Could not deduce (DrawEnv (t0, t1) m) arising from a use of `box'
from the context (DrawEnv Pos m)
bound by the type signature for draw :: DrawEnv Pos m => m ()
at Code/Interfaces3.hs:63:9-29
The type variables `t0', `t1' are ambiguous
Relevant bindings include
draw :: m () (bound at Code/Interfaces3.hs:64:1)
Note: there is a potential instance available:
instance DrawEnv Pos IO -- Defined at Code/Interfaces3.hs:56:10
In a stmt of a 'do' block: box (10.0, 10.0) (100.0, 100.0)
In the expression:
do { clear;
box (10.0, 10.0) (100.0, 100.0);
line (10.0, 10.0) (100.0, 50.0) }
In an equation for `draw':
draw
= do { clear;
box (10.0, 10.0) (100.0, 100.0);
line (10.0, 10.0) (100.0, 50.0) }
My question is why GHC doesn't accept this given the Pos constraint?
This class definition won't work, because the type of
clear
doesn't mention the type variablep
, so it is impossible to instantiateclear
with a concrete type. Adding type signatures tobox
orline
won't help - evenclear :: IO ()
will produce a type error.This could be fixed by adding a function dependency to your class:
Then the rest of your code compiles. Alternatively, you could split your class into two classes: