问题 Control.Lens中的“非法多态或限定类型”


我正在和我一起工作 Control.Lens。我写的实际功能相当复杂,但出于这个问题的目的,我把它归结为一个最小的失败例子:

import Control.Lens    

exampleFunc :: Lens s t a b -> String
exampleFunc _ = "Example"

这无法编译,产生以下错误消息:

Illegal polymorphic or qualified type: Lens s t a b
Perhaps you intended to use -XRankNTypes or -XRank2Types
In the type signature for `exampleFunc':
  exampleFunc :: Lens s t a b -> String

为什么这是非法的?它看起来非常类似于以下内容  编译:

import Data.Maybe

exampleFunc' :: Maybe (s, t, a, b) -> String
exampleFunc' _ = "Example"

所以我假设差异在于定义 Lens。但是呢 Lens 打字 exampleFunc的类型是非法的?我有一种潜在的怀疑,它与它有关 Functor 资格定义 Lens,但我可能是错的。供参考, 定义 的 Lens 是:

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t

所以,我必须以某种方式满足 Functor 在我的定义中的资格 exampleFunc?如果是这样,怎么样?我没有看到我的类型签名中的哪个位置我有机会声明这个约束。或者我可能走错了路,我的问题与我的问题无关 Functor 约束。

我已经阅读了有关“非法多态等”错误消息的所有Stack Overflow问题。也许这是我对Haskell缺乏熟悉的表现,但我看不出任何这些问题适用于我目前的情况。

我也没能找到任何关于错误信息通常意味着什么的文档。


1744
2017-11-20 19:28


起源

RankNTypes 是编译器建议时可以安全启用的扩展之一。对于许多其他扩展也是如此,尽管可能不适用 OverlappingInstances & IncoherentInstances。 - leftaroundabout


答案:


镜头使用等级2类型,你可以在箭头左侧使用它,所以要使用任何类型的镜头类型你必须使它合法甚至说出类似的东西

(forall a. foo) -> bar

哪个你也可以

{-# LANGUAGE RankNTypes #-} -- Rank2Types is a synonym for RankNTypes

在文件的顶部。没有它,即使使用镜头类型同义词也是违法的,因为它们使用了您必须启用的语言的一部分。


9
2017-11-20 19:43



这不是负面立场的意思。例如,在 (a -> r) -> r, a 出现在积极的位置。 - Ben Millwood
@BenMillwood这是一个简化版本,是的,负面和正面的位置表现得像乘法,因为负面位置的负面位置是一个正面的位置。但对于这个例子,我觉得简化的描述就足够了。 - jozefg


exampleFunc 无法编译,因为 Lens 类型同义词是多态的,并且在签名中出现在所谓的“负位置”,也就是说,在左侧 ->

您可以使用 Lens 在一个类型签名甚至没有 RankNTypes 上。这个类型检查:

import Control.Lens

lensy :: Lens' (a,b) a 
lensy = _1

但这没有成功:

oops :: Lens' (a,b) a -> Int
oops = const 5 

为什么?出于同样的原因,如果没有这样做,也无法进行类型检查 RankNTypes

{-# LANGUAGE ExplicitForAll #-}

fails :: (forall a. a -> Int) -> Int
fails = undefined

在这里 forall 处于负面位置,并且范围仅在 a -> Int。它是 履行 的 fails而不是 呼叫者 的 fails,选择类型的人 a。调用者必须提供适用于所有人的参数函数 a。此功能 需要RankNTypes扩展名

当。。。的时候 forall 范围超过整个签名(如何 Lens 孤立地定义)没有必要 RankNTypes。这个类型检查:

{-# LANGUAGE ExplicitForAll #-}

typechecks :: forall a. (a -> Int) -> Int
typechecks = undefined

但是这个功能与前一个功能不同,因为这里是 呼叫者 谁选择的类型 a。他可以传递一个仅适用于特定的参数函数 a

exampleFunc' 工作是因为,当没有 forall 是指定的,有隐含的 foralls 对于每个变量,范围在整个签名上。

这个解释来自Haskell邮件列表可能很有用。


7
2017-11-20 21:48