如何看待是多态函数的记录字段?

时间:2018-08-23 12:21:14

标签: haskell lens

我刚刚安装了lens库,因此可以轻松地set使用嵌套数据结构。但是,我遇到了一个问题。这是一个演示我的问题的最小示例

以下代码无法编译:

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens    

data MyRecord = MyRecord 
  { _func :: forall . a -> a
  }

makeLenses ''MyRecord

changeMyRecord :: MyRecord -> MyRecord
changeMyRecord r = r & func .~ id

错误是No Instance for (Contravariant Identity) arising from use of 'func'

我看过Contravariant,而且我很确定自此以后我不可能创建该实例

class Contravariant f where
  contramap :: (a -> b) -> f b -> f a

即如果f = \x -> x我看不到要在哪里找到要应用于函数参数a

的类型(a-> b)的东西

有没有其他方法可以使用镜头来修改MyRecord?或者我是否可以以某种方式避免使用RankNTypes,但仍然在记录中绕过多态_func?还是其他?

记录更新语法已不复存在-想象MyRecord嵌套得很深。

回答时请假设很少有haskell知识,尤其是我今天才开始看镜头库

1 个答案:

答案 0 :(得分:5)

lens在这里引人入胜–无法使用func作为类型的镜头(或其他具有写功能的光学元件)

func :: Lens' MyRecord (a -> a)

因为这意味着您可以放入任何具体类型的内部功能,例如

changeMyRecord :: MyRecord -> MyRecord
changeMyRecord r = r & func .~ ((+1) :: Int -> Int)

因此,它使func只是一个吸气剂

func :: Getter' MyRecord (a -> a)

...没关系,因为通用多态函数 可以用于任何类型,所以可以使用以下功能:

useMyRecord :: MyRecord -> String
useMyRecord r = show (r^.func $ 1 :: Int)

看到了

type Getter s a = ∀ f. (Contravariant f, Functor f) => (a -> f a) -> s -> f s

这就是Contravariant约束的来源。 No Instance for Contravariant错误消息只是Can't use a ‘Getter’ as a ‘Setter’的VanLaarhoven-Kmett-ish。

您实际上想要 拥有的当然是

func :: Lens' MyRecord (∀ a . a -> a)

但是不幸的是,这是impredicative type,Haskell doesn't support。即,它将扩展为

func :: ∀ f . Functor f => ((∀ a . a -> a) -> f (∀ a . a -> a)) -> MyRecord -> f MyRecord

请注意,中有一个f

要获取这种多态场透镜的语义,您需要将其包装为Rank-0类型:

newtype PolyEndo = PolyEndo { getPolyEndo :: ∀ a . a -> a }

data MyRecord = MyRecord 
  { _func :: PolyEndo
  }

makeLenses ''MyRecord
-- func :: Lens' MyRecord PolyEndo