如何镜头到多态函数的记录字段上?



我刚刚安装了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是深度嵌套的。

回答时请假设很少有哈斯克尔知识,特别是我今天才开始看镜头库

lens在这里拉了一个黑客 - 不可能将func用作具有类型类型的镜头(或其他具有写入能力的光学器件(

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

因为这意味着你可以把任何具体类型的内函数放进去,比如

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

所以相反,它使func只是一个getter

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错误消息只是VanLaarhoven-Kmett-ishCan't use a ‘Getter’ as a ‘Setter’

你真正想要当然是

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

但不幸的是,这是一种非预测类型,Haskell不支持。也就是说,它将扩展到

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

最新更新