Haskell 转换镜头';a b进入透镜';a(也许b)

Haskell 转换镜头';a b进入透镜';a(也许b),haskell,lenses,haskell-lens,Haskell,Lenses,Haskell Lens,我有几个数据结构,比如 data Data1=Data1 {u data1Field::Int --更多领域 }推导(等式,显示) 生成“数据1” 数据Data2=Data2 {u data2Field::Int --更多领域 }推导(等式,显示) 生成数据2 --更相似的数据类型 因此,我决定编写一个简单的类型类,使其更易于编写 类HasField a,其中 字段::镜头“a Int” 实例HasField Data1,其中 字段=数据1字段 实例HasField Data2,其中 字段=数

我有几个数据结构,比如

data Data1=Data1
{u data1Field::Int
--更多领域
}推导(等式,显示)
生成“数据1”
数据Data2=Data2
{u data2Field::Int
--更多领域
}推导(等式,显示)
生成数据2
--更相似的数据类型
因此,我决定编写一个简单的类型类,使其更易于编写

类HasField a,其中
字段::镜头“a Int”
实例HasField Data1,其中
字段=数据1字段
实例HasField Data2,其中
字段=数据2字段
但后来我遇到了一个问题,其中一些结构将相应的字段作为可选字段

data Data3=Data3
{u data3Field::可能是Int
--更多领域
}推导(等式,显示)
生成数据3
现在我不能再使用type类了。由于有大约相同数量的数据类型具有可选字段,因此我决定最好更改typeclass:

类HasField a,其中
字段::镜头“a”(可能是Int)
实例HasField Data3,其中
字段=数据3字段
但由于我对镜头库不是很有经验,我一直在想如何使这种新镜头与
Data1
Data2
的类型配合使用。理想情况下,我希望能够查看它并获得任何类型的
Maybe Int
值,当设置时,我希望
Just x
Data1
Data2
的字段设置为
x
,并且在传递
Nothing
时,这两种类型都不可操作

这是可能的使用现有的组合或我将不得不写自己的镜头?我很乐意这样做,但是现有的大多数教程都使用TH,并对手工编写的细节进行了润色


我使用GHC 7.6.3和
lens
3.10.

作为shachaf的后续版本

class HasFieldA d where
  field :: Traversal' d Int

instance HasFieldA Data1 where
  field = data1Field -- Lens's are Traversals

instance HasFieldA Data3 where
  field = data3Field . _Just
然后是
^?
运算符或
^..
运算符

getField :: HasFieldA d => d -> Maybe Int
getField = d ^? field -- or preview field d
去拿

要设置可选字段,需要另一个函数

class SetFieldA d where
  setField :: Setter' d Int
instance SetFieldA Data3 where
  setField = set data3Field . Just

你根本不能这么做,因为这是非法镜头。镜头法则:
view l(set l x s)=x
,因此,如果将其设置为
Nothing
,则在查看时必须获得
Nothing
。但这可能是您实际上想要一个
遍历
(这类似于镜头对0个或多个值的遍历,而不仅仅是一个值)。当您有一个
遍历
时,您可以使用
预览
来查看
可能
设置
来设置。@shachaf我该如何为此编写
遍历
?此外,如果这些记录有多个这样的字段,会不会有问题?@shachaf或者,有什么好的阅读材料可以帮助我理解如何编写这些类型的
遍历
s?这不允许将“无”字段值更改为“仅”字段值though@bennofs这是正确的。这使我可以为
Data1
Data2
设置字段,但不能为
Data3
@bennofs@bheklillr设置字段,如果我们这样做,我们就会违反遍历的规则。如果我们设置了一个
Nothing
字段,那么后续的遍历会受到影响,那么我们需要保留与开始时相同数量的目标。@jozefg那么有没有办法仍然满足镜头定律,并有一个易于使用的组合器,可以同时对这两种情况都起作用?还是我只是走错了路,试图让镜头做不该做的事?@bheklillr我想你是的。我现在就用这个,看看你在哪里