我有以下定义:
{-# LANGUAGE ExistentialQuantification #-}
module Test
where
class Named a where
name :: a -> String
data Wrap = forall a . (Named a, Read a) => Wrap (a -> IO ())
我想为Named
编写Wrap
个实例。下一个不起作用:
instance Named Wrap where
name (Wrap named) =
let a = undefined
_ = named a
in name a
错误:
Could not deduce (Named a0) arising from a use of ‘name’
from the context (Named a, Read a)
但是以下工作:
instance Named Wrap where
name (Wrap named) =
let a = read undefined
_ = named a
in name a
我看到的唯一区别是a
在read
中处于协变位置,但在name
中是逆变的位置。为什么第一个实例声明不起作用?
答案 0 :(得分:6)
第一个实例不起作用,因为它没有触发单态限制,因此a
获得一个多态类型,在{{1}中实例化以不同方式 }和named a
。另一方面,在编写name a
时,我们发现a = read undefined
具有类型限制类型,因此单态限制启动,我们必须为a
选择特定类型;由于a
唯一标识此类型,因此选择该类型,而不是named a
中的其他类型实例化。
您可以通过启用name a
来验证这是正确的解释,从而导致read
版本失败。
您可以使用lambda而不是let来解决问题,如:
NoMonomorphismRestriction
(通常,instance Named Wrap where
name (Wrap named) = (\a -> (\_ -> name a) (named a)) undefined
与let x = e in e'
相同,前提是(\x -> e') e
是单态的而不是递归的,我在此处执行了两次重写。)
但是,如果可能的话,我会建议更严肃地重新设计你的方法以完全避免x
。标准技巧是:
undefined
但是,class Named a where
name :: proxy a -> String
proxyForFun :: (a -> IO ()) -> Proxy a
proxyForFun _ = Proxy
instance Named Wrap where
name (Wrap named) = name (proxyForFun named)
的类型非常严格:不再可能编写name
检查其参数的实例,因此如果这是您需要的功能,则此方法将无效。