Yur*_*ras 2 haskell existential-type typeclass
我有以下定义:
{-# LANGUAGE ExistentialQuantification #-}
module Test
where
class Named a where
name :: a -> String
data Wrap = forall a . (Named a, Read a) => Wrap (a -> IO ())
Run Code Online (Sandbox Code Playgroud)
我想写一个Named实例Wrap.下一个不起作用:
instance Named Wrap where
name (Wrap named) =
let a = undefined
_ = named a
in name a
Run Code Online (Sandbox Code Playgroud)
错误:
Could not deduce (Named a0) arising from a use of ‘name’
from the context (Named a, Read a)
Run Code Online (Sandbox Code Playgroud)
但以下工作:
instance Named Wrap where
name (Wrap named) =
let a = read undefined
_ = named a
in name a
Run Code Online (Sandbox Code Playgroud)
我看到的唯一区别a是在协变位置read,但在逆变的位置name.为什么第一个实例声明不起作用?
第一个实例不起作用,因为它不会触发单态限制,因此a获取一个多态类型,在和中实例化的方式不同.另一方面,在写入时,我们发现它具有类型类限制类型,因此单态限制启动,我们必须选择特定类型; 由于唯一地标识了这种类型,因此选择了该类型,并且不在其他类型中实例化.named aname aa = read undefinedaanamed aname a
您可以read通过打开NoMonomorphismRestriction以验证这是正确的解释来导致版本失败.
您可以使用lambda而不是let来解决问题,如:
instance Named Wrap where
name (Wrap named) = (\a -> (\_ -> name a) (named a)) undefined
Run Code Online (Sandbox Code Playgroud)
(一般来说,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)
Run Code Online (Sandbox Code Playgroud)
但是,类型name是非常严格的:不再可能编写name检查其参数的实例,因此如果这是您需要的功能,则此方法将不起作用.
| 归档时间: |
|
| 查看次数: |
87 次 |
| 最近记录: |