具有逆变位置变量的存在包装器的实例

时间:2015-08-06 18:57:49

标签: haskell typeclass existential-type

我有以下定义:

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

我看到的唯一区别是aread中处于协变位置,但在name中是逆变的位置。为什么第一个实例声明不起作用?

1 个答案:

答案 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检查其参数的实例,因此如果这是您需要的功能,则此方法将无效。