在下面的程序中test₁
将无法编译,但test₂
将会编译。原因似乎是因为forall s.
中的withModulus₁
。由于s
,似乎withModulus₁
对forall s.
的每次调用都是不同的类型。为什么会这样?
{-# LANGUAGE
GADTs
, KindSignatures
, RankNTypes
, TupleSections
, ViewPatterns #-}
module Main where
import Data.Reflection
newtype Modulus :: * -> * -> * where
Modulus :: a -> Modulus s a
deriving (Eq, Show)
newtype M :: * -> * -> * where
M :: a -> M s a
deriving (Eq, Show)
add :: Integral a => Modulus s a -> M s a -> M s a -> M s a
add (Modulus m) (M a) (M b) = M (mod (a + b) m)
mul :: Integral a => Modulus s a -> M s a -> M s a -> M s a
mul (Modulus m) (M a) (M b) = M (mod (a * b) m)
unM :: M s a -> a
unM (M a) = a
withModulus₁ :: a -> (forall s. Modulus s a -> w) -> w
withModulus₁ m k = k (Modulus m)
withModulus₂ :: a -> (Modulus s a -> w) -> w
withModulus₂ m k = k (Modulus m)
test₁ = withModulus₁ 89 (\m ->
withModulus₁ 7 (\m' ->
let
a = M 131
b = M 127
in
unM $ add m' (mul m a a) (mul m b b)))
test₂ = withModulus₂ 89 (\m ->
withModulus₂ 7 (\m' ->
let
a = M 131
b = M 127
in
unM $ add m' (mul m a a) (mul m b b)))
以下是错误消息:
Modulus.hs:41:29: error:
• Couldn't match type ‘s’ with ‘s1’
‘s’ is a rigid type variable bound by
a type expected by the context:
forall s. Modulus s Integer -> Integer
at app/Modulus.hs:(35,9)-(41,52)
‘s1’ is a rigid type variable bound by
a type expected by the context:
forall s1. Modulus s1 Integer -> Integer
at app/Modulus.hs:(36,11)-(41,51)
Expected type: M s1 Integer
Actual type: M s Integer
• In the second argument of ‘add’, namely ‘(mul m a a)’
In the second argument of ‘($)’, namely
‘add m' (mul m a a) (mul m b b)’
In the expression: unM $ add m' (mul m a a) (mul m b b)
• Relevant bindings include
m' :: Modulus s1 Integer (bound at app/Modulus.hs:36:28)
m :: Modulus s Integer (bound at app/Modulus.hs:35:27)
|
41 | unM $ add m' (mul m a a) (mul m b b)))
| ^^^^^^^^^
答案 0 :(得分:5)
简单地说,一个功能
foo :: forall s . T s -> U s
让其调用者选择s
类型。实际上,它适用于所有类型s
。相比之下,
bar :: (forall s . T s) -> U
要求其调用者提供参数x :: forall s. T s
,即可以对所有类型s
起作用的多态值。这意味着bar
将选择s
类型。
例如,
foo :: forall a. a -> [a]
foo x = [x,x,x]
很明显。取而代之的是,
bar :: (forall a. a->a) -> Bool
bar x = x 12 > length (x "hello")
更微妙。在此处,bar
首先使用x
为a ~ Int
选择x 12
,然后再次使用x
为a ~ String
选择x "hello"
。< / p>
另一个例子:
bar2 :: Int -> (forall a. a->a) -> Bool
bar2 n x | n > 10 = x 12 > 5
| otherwise = length (x "hello") > 7
此处a
选择为Int
或String
,具体取决于n > 10
。
您自己的类型
withModulus₁ :: a -> (forall s. Modulus s a -> w) -> w
声明必须允许withModulus₁
选择s
任意类型。将此称为
withModulus₁ arg (\m -> ...)
m
将有Modulus s0 a
类型,其中a
由调用者选择,而s
由withModulus₁
本身选择。 ...
必须与withModulus₁
可能选择的任何选项兼容。
如果我们嵌套电话怎么办?
withModulus₁ arg (\m1 -> ...
withModulus₁ arg (\m2 -> ...)
...
)
现在,m1 :: Modulus s0 a
和以前一样。进一步m2 :: Modulus s1 a
其中s1
由withModulus₁
的最里面调用选择。
关键在于,没有保证s0
被选为与s1
相同。每个电话可能会做出不同的选择:例如,确实如此bar2
。
因此,编译器不能假设s0
和s1
相等。因此,如果我们调用一个需要它们相等的函数,比如add
,我们会得到一个类型错误,因为这会限制两个s
调用选择withModulus₁
的自由。 / p>