具有逆变位置变量的存在包装实例
Instance for existential wrapper with a variable in contravariant position
我的定义如下:
{-# LANGUAGE ExistentialQuantification #-}
module Test
where
class Named a where
name :: a -> String
data Wrap = forall a . (Named a, Read a) => Wrap (a -> IO ())
我想为 Wrap
编写 Named
实例。下一个不行:
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
中处于逆变位置。为什么一审声明不起作用?
第一个实例不起作用,因为它不会触发单态限制,所以 a
得到一个多态类型,它在 [=13] 中以 不同方式 实例化=] 和 name a
。另一方面,当写 a = read undefined
时,我们发现 a
有一个 typeclass-restricted 类型,所以单态限制开始了,我们必须为 a
选择一个特定的类型;由于 named a
唯一标识此类型,因此选择该类型并且不会在 name a
.
中以不同类型实例化
您可以通过打开 NoMonomorphismRestriction
来验证这是正确的解释,从而导致 read
版本失败。
您可以使用 lambda 而不是 let 来解决这个问题,例如:
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
的类型非常受限:不再可能编写 name
检查其参数的实例,因此如果这是您需要的功能,则此方法不会工作。
我的定义如下:
{-# LANGUAGE ExistentialQuantification #-}
module Test
where
class Named a where
name :: a -> String
data Wrap = forall a . (Named a, Read a) => Wrap (a -> IO ())
我想为 Wrap
编写 Named
实例。下一个不行:
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
中处于逆变位置。为什么一审声明不起作用?
第一个实例不起作用,因为它不会触发单态限制,所以 a
得到一个多态类型,它在 [=13] 中以 不同方式 实例化=] 和 name a
。另一方面,当写 a = read undefined
时,我们发现 a
有一个 typeclass-restricted 类型,所以单态限制开始了,我们必须为 a
选择一个特定的类型;由于 named a
唯一标识此类型,因此选择该类型并且不会在 name a
.
您可以通过打开 NoMonomorphismRestriction
来验证这是正确的解释,从而导致 read
版本失败。
您可以使用 lambda 而不是 let 来解决这个问题,例如:
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
的类型非常受限:不再可能编写 name
检查其参数的实例,因此如果这是您需要的功能,则此方法不会工作。