看似正确的类型签名在 where 块中被拒绝

Seemingly correct type signature refused in where block

我一直在研究 Haskell 中的延续传球风格。将 Cont monad 分开并删除类型包装器有助于我理解实现。这是代码:

{-# LANGUAGE ScopedTypeVariables #-}

import Control.Monad (ap)


newtype Cont r a = Cont {runCont :: (a -> r) -> r}

instance Functor (Cont r) where
  fmap f ka = ka >>= pure . f

instance Applicative (Cont r) where
  (<*>) = ap
  pure = return

instance Monad (Cont r) where

  ka >>= kab = Cont kb'
    where
      -- kb' :: (b -> r) -> r
      kb' hb = ka' ha
        where
          -- ha :: (a -> r)
          ha a = (kab' a) hb

      -- ka' :: (a -> r) -> r
      ka' = runCont ka

      -- kab' :: a -> (b -> r) -> r
      kab' a = runCont (kab a)


  return a = Cont ka'
    where
      -- ka' :: (a -> r) -> r
      ka' ha = ha a

此代码编译(使用 GHC 8.0.2)并且一切正常。但是,一旦我取消注释 where 块中的任何(现在已注释的)类型签名,我就会收到错误消息。例如,如果我取消注释行

      -- ka' :: (a -> r) -> r

我得到:

    • Couldn't match type ‘a’ with ‘a1’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          (>>=) :: forall a b. Cont r a -> (a -> Cont r b) -> Cont r b
        at cont.hs:19:6
      ‘a1’ is a rigid type variable bound by
        the type signature for:
          ka' :: forall a1. (a1 -> r) -> r
        at cont.hs:27:14
      Expected type: (a1 -> r) -> r
        Actual type: (a -> r) -> r
    • In the expression: runCont ka
      In an equation for ‘ka'’: ka' = runCont ka
      In an equation for ‘>>=’:
          ka >>= kab
            = Cont kb'
            where
                kb' hb
                  = ka' ha
                  where
                      ha a = (kab' a) hb
                ka' :: (a -> r) -> r
                ka' = runCont ka
                kab' a = runCont (kab a)
    • Relevant bindings include
        ka' :: (a1 -> r) -> r (bound at cont.hs:28:7)
        kab' :: a -> (b -> r) -> r (bound at cont.hs:31:7)
        kab :: a -> Cont r b (bound at cont.hs:19:10)
        ka :: Cont r a (bound at cont.hs:19:3)
        (>>=) :: Cont r a -> (a -> Cont r b) -> Cont r b
          (bound at cont.hs:19:3)
Failed, modules loaded: none.

所以我尝试使用类型通配符让编译器告诉我应该放什么类型签名。因此,我尝试了以下签名:

      ka' :: _

出现以下错误:

    • Found type wildcard ‘_’ standing for ‘(a -> r) -> r’
      Where: ‘r’ is a rigid type variable bound by
               the instance declaration at cont.hs:15:10
             ‘a’ is a rigid type variable bound by
               the type signature for:
                 (>>=) :: forall a b. Cont r a -> (a -> Cont r b) -> Cont r b
               at cont.hs:19:6
      To use the inferred type, enable PartialTypeSignatures
    • In the type signature:
        ka' :: _
      In an equation for ‘>>=’:
          ka >>= kab
            = Cont kb'
            where
                kb' hb
                  = ka' ha
                  where
                      ha a = (kab' a) hb
                ka' :: _
                ka' = runCont ka
                kab' a = runCont (kab a)
      In the instance declaration for ‘Monad (Cont r)’
    • Relevant bindings include
        ka' :: (a -> r) -> r (bound at cont.hs:28:7)
        kab' :: a -> (b -> r) -> r (bound at cont.hs:31:7)
        kab :: a -> Cont r b (bound at cont.hs:19:10)
        ka :: Cont r a (bound at cont.hs:19:3)
        (>>=) :: Cont r a -> (a -> Cont r b) -> Cont r b
          (bound at cont.hs:19:3)
Failed, modules loaded: none.

现在我真的很困惑,编译器告诉我 ka' 的类型是 (a -> r) -> r 但是当我尝试用这种类型显式注释 ka' 时, 编译失败。起初我以为我错过了 ScopedTypeVariables 但它似乎没有什么不同。

这是怎么回事?

编辑 这类似于问题“”,因为它需要显式 forall 来绑定类型变量。但是它不是重复的,因为这个问题的答案也需要 InstanceSigs 扩展名。

有道理。毕竟,那些 ab 是从哪里来的?我们无法知道它们与 (>>=)return 的多态性有关。不过,如评论中所述,修复起来很容易:给出 (>>=)return 类型签名,其中提到 ab,加入必要的语言扩展,嘿转眼间:

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE InstanceSigs #-}

import Control.Monad (ap)


newtype Cont r a = Cont {runCont :: (a -> r) -> r}

instance Functor (Cont r) where
  fmap f ka = ka >>= pure . f

instance Applicative (Cont r) where
  (<*>) = ap
  pure = return

instance Monad (Cont r) where

  (>>=) :: forall a b. Cont r a -> (a -> Cont r b) -> Cont r b
  ka >>= kab = Cont kb'
    where
      kb' :: (b -> r) -> r
      kb' hb = ka' ha
        where
          ha :: (a -> r)
          ha a = (kab' a) hb

      ka' :: (a -> r) -> r
      ka' = runCont ka

      kab' :: a -> (b -> r) -> r
      kab' a = runCont (kab a)


  return :: forall a. a -> Cont r a
  return a = Cont ka'
    where
      ka' :: (a -> r) -> r
      ka' ha = ha a

我觉得所有这些 kaha 中都有一个龙珠笑话,但笑话逃脱了 me