2015-06-22 2 views
0

Я пытаюсь реализовать «DrawEnv» типа класса индексированный по типу точки:Как использовать GHC MultiParamTypeClass

{-# 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) 

Однако GHC не устраивает:

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) } 

Мой вопрос, почему GHC не принимает это с учетом ограничения Pos?

+1

У меня нет ошибок с GHC 7.8 и 7.10. –

ответ

2

Данное определение класса не будет работать, так как тип clear не упоминает переменную типа p, поэтому невозможно создать экземпляр clear с конкретным типом. Добавление подписей типа к box или line не поможет - даже clear :: IO() приведет к ошибке типа.

Это может быть исправлено путем добавления функции зависимости к классу:

class Monad m => DrawEnv p m | m -> p where 

Тогда остальная часть кода компилируется. В качестве альтернативы вы можете разделить свой класс на два класса:

class Monad m => MonadDraw m where 
    putStringLn :: String -> m() 

    clear :: m() 
    clear = putStringLn "Clear" 

class DrawEnv p where 
    box  :: MonadDraw m => p -> p -> m() 
    line :: MonadDraw m => p -> p -> m() 

instance (Fractional a, Show a, Fractional b, Show b) => DrawEnv (a,b) where 
    box  p0 p1 = putStringLn $ "Box " ++ show p0 ++ " " ++ show p1 
    line p0 p1 = putStringLn $ "Line " ++ show p0 ++ " " ++ show p1 

draw :: MonadDraw m => m() 
draw = do 
    clear 
    box (10.0,10.0) (100.0,100.0) 
    line (10.0,10.0) (100.0,50.0) 
+0

Использование зависимости функции - отличное решение. Благодаря! – mbrodersen

2

Код неоднозначный. В частности, мы не знаем тип (10.0,10.0). Например, это может быть (Double,Double). Это самый общий тип: (Fractional a,Fractional b) => (a,b).

Решение этой проблемы заключается в написании

box ((10.0,10.0) :: Pos) ((100.0,100.0)::Pos) 

вместо того, чтобы фиксировать и другие линии аналогичным образом.

Смежные вопросы