我试图理解MultiParamTypeClasses
和FunctionalDependencies
,以下内容让我感到很明显:
{-# LANGUAGE MultiParamTypeClasses
, FunctionalDependencies
, TypeOperators #-}
import Data.Type.Equality
class C a b | a -> b
fob :: (C a b, C a b') => proxy a -> b :~: b'
fob _ = Refl
不幸的是,这不起作用; GHC没有从该背景中得出b ~ b'
。有没有办法使这项工作,或功能依赖不是“内部”可用?
答案 0 :(得分:4)
我不认为这个事实(如fob
的类型所述)实际上是正确的。由于类型类的开放世界属性,您可以违反带有模块边界的fundep。
以下示例显示。此代码仅使用GHC 7.10.3进行了测试(旧版本中的fundeps被大量破解 - 不知道接下来会发生什么)。假设您确实可以实现以下内容:
module A
(module A
,module Data.Type.Equality
,module Data.Proxy
)where
import Data.Type.Equality
import Data.Proxy
class C a b | a -> b
inj_C :: (C a b, C a b') => Proxy a -> b :~: b'
inj_C = error "oops"
然后再增加几个模块:
module C where
import A
instance C () Int
testC :: C () b => Int :~: b
testC = inj_C (Proxy :: Proxy ())
和
module B where
import A
instance C () Bool
testB :: C () b => b :~: Bool
testB = inj_C (Proxy :: Proxy ())
和
module D where
import A
import B
import C
oops :: Int :~: Bool
oops = testB
oops_again :: Int :~: Bool
oops_again = testC
Int :~: Bool
显然不正确,因此矛盾,inj_C
不可能存在。
如果您不从定义它的模块中导出类inj_C
,我相信您仍然可以安全地使用unsafeCoerce
写C
。我已经使用过这种技术,并且已经进行了广泛的尝试,并且无法写出矛盾。不是说这是不可能的,但至少是非常困难和罕见的边缘情况。
答案 1 :(得分:3)
您不需要求助于多个模块来欺骗功能依赖性检查程序。以下是仍然使用HEAD构建的错误fundeps的两个示例。它们改编自GHC测试套件。
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies,
FlexibleInstances, FlexibleContexts,
UndecidableInstances, DataKinds, PolyKinds,
GADTs #-}
module M where
data K x a = K x
class Het a b | a -> b where
het :: m (f c) -> a -> m b
instance Het t t where het = undefined
class GHet (a :: * -> *) (b :: * -> *) | a -> b
instance GHet (K a) (K [a])
instance Het a b => GHet (K a) (K b)
data HBool = HFalse | HTrue
class TypeEq x y b | x y -> b
instance {-# OVERLAPS #-} (HTrue ~ b) => TypeEq x x b
instance {-# OVERLAPS #-} (HFalse ~ b) => TypeEq x y b
fundep检查器仍然比以前好多了!