什么时候不是所有人

时间:2017-11-04 00:16:48

标签: haskell

在下面的程序中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)))
   |                             ^^^^^^^^^

1 个答案:

答案 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首先使用xa ~ Int选择x 12,然后再次使用xa ~ 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选择为IntString,具体取决于n > 10

您自己的类型

withModulus₁ :: a -> (forall s. Modulus s a -> w) -> w

声明必须允许withModulus₁选择s任意类型。将此称为

withModulus₁ arg (\m -> ...)

m将有Modulus s0 a类型,其中a由调用者选择,而swithModulus₁本身选择。 ...必须与withModulus₁可能选择的任何选项兼容。

如果我们嵌套电话怎么办?

withModulus₁ arg (\m1 -> ...
   withModulus₁ arg (\m2 -> ...)
   ...
     )

现在,m1 :: Modulus s0 a和以前一样。进一步m2 :: Modulus s1 a其中s1withModulus₁的最里面调用选择。

关键在于,没有保证s0被选为与s1相同。每个电话可能会做出不同的选择:例如,确实如此bar2

因此,编译器不能假设s0s1相等。因此,如果我们调用一个需要它们相等的函数,比如add,我们会得到一个类型错误,因为这会限制两个s调用选择withModulus₁的自由。 / p>