haskell - 在 where 子句中指定函数的类型

我有以下 Monad 实例,基于 these slides 中的 Material :

{-# LANGUAGE InstanceSigs #-}

newtype Iter a = Iter { runIter :: Chunk -> Result a }

instance Monad Iter where
  return = Iter . Done
  (>>=) :: Iter a -> (a -> Iter b) -> Iter b
  (Iter iter0) >>= fiter = Iter $ \chunk -> continue (iter0 chunk)
    where continue :: Result a -> Result b
          continue (Done x rest)     = runIter (fiter x) rest
          continue (NeedInput iter1) = NeedInput (iter1 >>= fiter)
          continue (NeedIO ior)      = NeedIO (liftM continue ior)
          continue (Failed e)        = Failed e

这将给出以下错误:

• Couldn't match type ‘b’ with ‘b1’
  ‘b’ is a rigid type variable bound by
    the type signature for:
      (>>=) :: forall a b. Iter a -> (a -> Iter b) -> Iter b
    at Iteratee.hs:211:12
  ‘b1’ is a rigid type variable bound by
    the type signature for:
      continue :: forall a1 b1. Result a1 -> Result b1
    at Iteratee.hs:214:23
  Expected type: Result b1
    Actual type: Result b
• In the expression: runIter (fiter x) rest
  In an equation for ‘continue’:
      continue (Done x rest) = runIter (fiter x) rest
  In an equation for ‘>>=’:
      (Iter iter0) >>= fiter
        = Iter $ \ chunk -> continue (iter0 chunk)
        where
            continue :: Result a -> Result b
            continue (Done x rest) = runIter (fiter x) rest
            continue (NeedInput iter1) = NeedInput (iter1 >>= fiter)
            continue (NeedIO ior) = NeedIO (liftM continue ior)
            continue (Failed e) = Failed e
• Relevant bindings include
    continue :: Result a1 -> Result b1 (bound at Iteratee.hs:215:11)
    fiter :: a -> Iter b (bound at Iteratee.hs:212:20)
    (>>=) :: Iter a -> (a -> Iter b) -> Iter b
      (bound at Iteratee.hs:212:3)

令我更加困惑的是,如果我未定义 continue 但我分配了代码编译的类型。

我的猜测是这个问题是由continue actually having type引起的

continue :: forall a1 b1. Result a1 -> Result b1

因此,上述类型中的两个 ab 实际上是不同的。但是尽管如此,上面的 continue 必须有一个类型。那么我的问题是,当省略类型时,编译器分配的这个函数的类型是什么。

编辑:

如果显式传递了 iter 参数,那么代码会编译:

instance Monad Iter where
  return = Iter . Done
  (>>=) :: Iter a -> (a -> Iter b) -> Iter b
  (Iter iter0) >>= fiter0 = Iter $ \chunk -> continue fiter0 (iter0 chunk)
    where continue :: (a -> Iter b) -> Result a -> Result b
          continue fiter (Done x rest)     = runIter (fiter x) rest
          continue fiter (NeedInput iter1) = NeedInput (iter1 >>= fiter)
          continue fiter (NeedIO ior)      = NeedIO (liftM (continue fiter) ior)
          continue _ (Failed e)        = Failed e

但是我想避免显式传递参数,同时能够为 continue 提供一个类型。

最佳答案

在基本的 Haskell 中,每个类型签名都被隐式地普遍量化

foo :: Bool -> a -> a -> a
foo b x y = bar y
   where bar :: a -> a
         bar y | b         = x
               | otherwise = y 

实际意思是:

foo :: forall a. Bool -> a -> a -> a
foo b x y = bar y
   where bar :: forall a1. a1 -> a1
         bar y | b         = x
               | otherwise = y 

编译失败,因为x 不是a1 类型。

删除 bar 的类型签名使其可以编译,编译器将关联到 bar 正确的类型 a -> a where a没有普遍量化。请注意,这是一种编译器可以推断的类型,但用户无法编写。

这很不方便!

因此,ScopedTypeVarables GHC 扩展规避了这一点,允许编写

foo :: forall a. Bool -> a -> a -> a
foo b x y = bar y
   where bar :: a -> a
         bar y | b         = x
               | otherwise = y 

这里的第一个 forall a. 使 a 位于内部声明的范围内。此外,bar 的类型仍然是 a -> a 并且没有被普遍量化,因为 a 现在在范围内。因此,它可以编译,用户现在可以编写所需的类型注释。

https://stackoverflow.com/questions/43753864/

相关文章:

visual-c++ - 访问 CBitmap 对象中的单个像素

wifi - Arduino 非阻塞 Wifi 连接

centos7 - 在 LVM 之上扩展 GlusterFS

sql - 有条件的平均值

javascript - 在哪里发布代码以供开源使用?

haxe - 是否有 atexit() 等同于 Haxe?

pandas - Pyspark - 如何回填 DataFrame?

r - big.matrix 文档中不可重现的示例(应用)

list - 类型 ('a -> ' b) list 的函数 -> OCaml 中的 'a -> '

ibm-midrange - 在 AS400 中的程序之间传递可选参数