首页 > 解决方案 > 为什么这个镜头功能需要类型签名?

问题描述

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

{-# 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]

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

标签: haskellhaskell-lens

解决方案


问题在于它lens在两种情况下使用,view 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] s并且Identity s,不要统一,这就是错误消息的意思。)当 GHC 试图lensupdateRecord没有显式类型签名的情况下推断类型检查时,它推断出上面的第一个类型forlens基于其在中的使用,view但无法将其与中出现的第二种类型统一起来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")

推荐阅读