2

我有几个数据结构,比如

data Data1 = Data1
    { _data1Field :: Int
    -- More fields
    } deriving (Eq, Show)
makeLenses ''Data1

data Data2 = Data2
    { _data2Field :: Int
    -- More fields
    } deriving (Eq, Show)
makeLenses ''Data2

-- More similar data types

所以我决定写一个简单的类型类,让它更容易编写

class HasField a where
    field :: Lens' a Int

instance HasField Data1 where
    field = data1Field

instance HasField Data2 where
    field = data2Field

但是后来我遇到了一些问题,其中一些结构具有相应的字段作为可选

data Data3 = Data3
    { _data3Field :: Maybe Int
    -- More fields
    } deriving (Eq, Show)
makeLenses ''Data3

现在我不能再使用类型类了。由于具有该字段可选的数据类型的数量大致相同,因此我决定更改类型类会更好:

class HasField a where
    field :: Lens' a (Maybe Int)

instance HasField Data3 where
    field = data3Field

但是由于我对镜头库不是很有经验,所以我一直在弄清楚如何使这个新镜头与 和 的类型一起Data1工作Data2。理想情况下,我希望能够查看它并获取Maybe Int任何类型的值,并且在设置时我希望Just x将字段设置为xforData1并且Data2在传递时成为这两种类型的无操作Nothing

这可以使用现有的组合器还是我必须自己编写镜头?我这样做很好,但是大多数现有教程都使用 TH 并掩盖了手工编写的细节。

我正在使用 GHC 7.6.3 和lens3.10。

4

1 回答 1

2

作为 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
于 2013-11-04T18:57:03.893 回答