为什么这个镜头功能需要一个类型签名?

7

我正在编写一个使用lenses库的函数,但奇怪的是,当我删除类型注释时,代码无法编译。

{-# LANGUAGE TemplateHaskell, Rank2Types #-}

import Control.Lens
import Control.Monad.State

import Data.List (elemIndex)

data MyRecord = MyRecord { _someField :: [Int], _anotherField :: [String] }
makeLenses ''MyRecord

updateRecord :: Eq a => a -> Lens' b [a] -> (Int -> c) -> State b c
updateRecord elem lens f = do field <- view lens <$> get
                              case elemIndex elem field of
                                Just index -> return $ f index
                                Nothing -> do modify (over lens (++[elem]))
                                              return . f $ length field

当我取消注释 updateRecord 的类型签名时,会出现以下错误:
Couldn't match typeConst [a] s’ with ‘Identity s’
      Expected type: ASetter s s [a] [a]
        Actual type: Getting [a] s [a]

在这种情况下,为什么需要类型签名?

这个问题实际上类似于 https://dev59.com/_Ijca4cB1Zd3GeqP4vsK?rq=1 - Joe the Person
1个回答

8
问题在于 lens 在两个上下文中被使用,视图镜头 中需要具有以下类型:
Getting [a] s [a]
= ([a] -> Const [a] [a]) -> (s -> Const [a] s)

而在需要类型为over lens ...的情况下:

ASetter s s [a] [a]
= ([a] -> Identity [a]) -> (a -> Identity s)

很遗憾,这些类型无法统一。(特别地,这些类型的最右边部分,Const [a] sIdentity s,无法统一,这就是错误信息所述的内容。) 当 GHC 在没有显式类型声明的情况下推断 updateRecordlens 的类型时,在它在 view 中的使用基础上,它会推断出 lens 的第一个类型,但随后无法将其与在 over 中产生的第二个类型统一起来。
然而,尽管这些类型不统一,仍然有一个可以针对每个类型单独特化的高阶多态类型,即:
Lens' s a
= Lens s s a a = forall f. Functor f => (a -> f s) -> (a -> f s)

只要 GHC 能够通过显式类型签名单独推断出这种类型,它就能将这个更一般的类型与每个使用的类型统一起来。这只是高阶类型的一个基本限制之一。你可以在一个更小的例子中看到同样的现象。这个函数 "foo" 无法通过类型检查。
foo f = (f 10, f "hello")

但是加上类型签名就没问题了:

foo :: (forall a. a -> a) -> (Int, String)
foo f = (f 10, f "hello")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接