如何使用 GHC MultiParamTypeClass
How to use GHC MultiParamTypeClass
我正在尝试实现由点类型索引的 "DrawEnv" 类型 class:
{-# 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 约束?
代码不明确。具体来说,我们不知道 (10.0,10.0)
的类型。例如,它可以是 (Double,Double)
。最通用的类型是 (Fractional a,Fractional b) => (a,b)
.
解决办法是这样写
box ((10.0,10.0) :: Pos) ((100.0,100.0)::Pos)
改为类似地修复其他行。
这个class定义不行,因为clear
的类型没有提到类型变量p
,所以无法实例化clear
用一个具体的类型。向 box
或 line
添加类型签名无济于事 - 即使 clear :: IO ()
也会产生类型错误。
这可以通过向您的 class 添加函数依赖来解决:
class Monad m => DrawEnv p m | m -> p where
然后你的代码的其余部分编译。或者,您可以将 class 分成两个 classes:
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)
我正在尝试实现由点类型索引的 "DrawEnv" 类型 class:
{-# 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 约束?
代码不明确。具体来说,我们不知道 (10.0,10.0)
的类型。例如,它可以是 (Double,Double)
。最通用的类型是 (Fractional a,Fractional b) => (a,b)
.
解决办法是这样写
box ((10.0,10.0) :: Pos) ((100.0,100.0)::Pos)
改为类似地修复其他行。
这个class定义不行,因为clear
的类型没有提到类型变量p
,所以无法实例化clear
用一个具体的类型。向 box
或 line
添加类型签名无济于事 - 即使 clear :: IO ()
也会产生类型错误。
这可以通过向您的 class 添加函数依赖来解决:
class Monad m => DrawEnv p m | m -> p where
然后你的代码的其余部分编译。或者,您可以将 class 分成两个 classes:
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)