Haskell中命名字段的令人惊讶的类型推断

时间:2018-01-27 01:43:20

标签: haskell type-inference

考虑以下GHCi的转录本,版本8.2.2:

GHCi, version 8.2.2: http://www.haskell.org/ghc/  :? for help
Prelude> :set -XRankNTypes
Prelude> data Functor f = Functor { fmap :: forall a b. (a -> b) -> f a -> f b }
Prelude> :t fmap
fmap :: Functor f1 -> (a -> b) -> f2 a -> f2 b
Prelude> :t Functor map
Functor map :: Functor []
Prelude> :t fmap (Functor map)
fmap (Functor map) :: (a -> b) -> [a] -> [b]

如您所见,fmap的类型推断为Functor f1 -> (a -> b) -> f2 a -> f2 b。这是令人惊讶的,因为f1f2应该是相同的类型变量,但是没有f1 ~ f2约束。但是,如果您将fmap应用于Functor map类型Functor [],则结果仍然符合预期的类型(a -> b) -> [a] -> [b]。这里发生了什么?我希望fmap具有类型Functor f -> (a -> b) -> f a -> f b

1 个答案:

答案 0 :(得分:2)

我可以确认这个问题影响GHC 8.2.2但不影响8.0.2。 更新:看起来它已在8.4分支上修复。

它似乎是GHCi类型的签名显示错误,而不是真正的类型检查程序问题,因为如果您采用该程序:

{-# LANGUAGE RankNTypes #-}
module Inference where
data Functor f = Functor { fmap :: forall a b. (a -> b) -> f a -> f b }

并使用ghc -ddump-tc进行编译,您可以看到GHC推断出正确的类型:

TYPE SIGNATURES
  ...
  Inference.fmap ::
    forall (f :: * -> *).
    Inference.Functor f -> forall a b. (a -> b) -> f a -> f b

它似乎也不会影响GHC错误消息。如果添加以下行:

main = print Inference.fmap

生成包含类型的错误消息("没有(显示xxx)"的实例),您看到正确的类型。