给出这个整数的包装:
newtype MyProxy a = MyProxy Int
mkProxy :: Int -> MyProxy a
mkProxy a = MyProxy a
addProxy :: MyProxy a -> MyProxy a -> MyProxy a
addProxy (MyProxy a1) (MyProxy a2) = MyProxy $ a1+a2
我可以执行以下操作:
a = mkProxy 1
b = mkProxy 2
c = addProxy a b
因为幻像参数将统一。但是我想阻止这种统一并在c
行上造成类型错误。
ST monad使用rank2类型来实现类似的效果。我可以通过改变addProxy
的类型来做类似的事情。但我特别不想这样做。我想以某种方式注释a
类型变量,以防止它在addProxy
调用上统一。
Haskell有可能吗?这样的选择会有危险吗?
编辑:
让我详细说明一个部分解决方案(需要-XScopedTypeVariables
)。我可以将上面的代码重写为:
c :: forall a1 a2. MyProxy a1
c = addProxy a b
where
a = mkProxy 1 :: MyProxy a1
b = mkProxy 2 :: MyProxy a2
这正确导致c
上的类型错误,因为a1
和a2
无法统一。
但这有两个缺点:a
和b
无法在顶层定义;并且您必须明确指定结果类型mkProxy
。
是否有可能解决这些缺点?
答案 0 :(得分:2)
不,你不能,至少在没有向mkProxy
指定更多内容的情况下。
ST monad的工作方式是要求转义计算生成forall s. ST s a
类型的内容,以防止s
a
中出现mkInt 1
。
然而,在你的情况下,你正在做两个相同的计算,因此为每个计算生成一个不同的类型可以用来做邪恶的事情。例如,如果class Evil a b c | a, b -> c where
foo :: a -> b -> c
let x = mkProxy 1 in foo x x
每次调用时都生成了不同的类型,
foo (mkProxy 1) (mkProxy 1)
与
不同a
我们在代码中丢失了一些非常好的属性。
然而,我们可以做一些额外的工作,并使b
和 {-# LANGUAGE DataKinds #-}
data Nat = S Nat | Z
data Proxy (n :: Nat) a = Proxy a
based :: a -> Proxy Z a
based = Proxy
fresh :: Proxy n a -> a -> Proxy (S n) a
fresh (Proxy _) a = Proxy a
无法统一显示
a = based 1
b = fresh a 2
所以现在你需要做一些像
这样的事情{{1}}
答案 1 :(得分:0)
我认为存在类型变量适合于此。基本上,独立创建的两个存在类型变量不会统一。在下面的test
无法进行类型检查,因为它无法统一两个MyProxy
的类型变量。
{-# LANGUAGE ExistentialQuantification #-}
newtype MyProxy a = MyProxy Int
data Exists f = forall a. Exists (f a)
mkProxy :: Int -> Exists MyProxy
mkProxy a = Exists (MyProxy a)
addProxy :: MyProxy a -> MyProxy a -> MyProxy a
addProxy (MyProxy a1) (MyProxy a2) = MyProxy $ a1+a2
test :: Exists MyProxy -> Exists MyProxy -> Exists MyProxy
test (Exists a) (Exists b) = Exists (addProxy a b)