为什么此镜头功能需要类型签名?

时间:2019-07-16 22:12:00

标签: haskell lens

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

{-# 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 type ‘Const [a] s’ with ‘Identity s’
      Expected type: ASetter s s [a] [a]
        Actual type: Getting [a] s [a]

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

1 个答案:

答案 0 :(得分:8)

问题在于,lensview 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尝试推断{{1}的类型时}在没有显式类型签名的情况下对lens进行类型检查,它会根据updateRecord中的使用来推断上述lens的第一个类型,但无法将其与在view

但是,即使类型不统一,也有 可以分别专门针对每个对象的更高级别的多态类型,即:

over

只要GHC可以分别推断出这种类型,说,通过显式类型签名,它就可以将每种用途的这种更通用的类型统一起来。

这只是其中之一,是较高等级类型的基本限制。您可以通过一个较小的示例看到相同的现象。此功能Lens' s a = Lens s s a a = forall f. Functor f => (a -> f s) -> (a -> f s) 未进行类型检查:

foo

但是添加类型签名就可以了:

foo f = (f 10, f "hello")