如何使用Lens更新包含Maybe的Haskell嵌套记录
解决方案
1. 准备工作:启用扩展与导入模块
首先需要启用必要的Haskell扩展,并导入依赖的库模块:
{-# LANGUAGE TemplateHaskell #-} import Data.Text (Text) import Control.Lens (set, makeLenses, non) import Data.Monoid (Monoid(..))
2. 定义数据类型与自动生成Lens
使用TemplateHaskell自动生成数据类型的Lens(手动编写Lens也可,自动生成更高效):
data RecA = RecA { _aField1 :: Maybe Text , _aField2 :: Maybe RecB } deriving (Show) data RecB = RecB { _bField1 :: Maybe Text , _bField2 :: Maybe Text } deriving (Show) makeLenses ''RecA makeLenses ''RecB
3. 定义Monoid实例
为嵌套类型定义Monoid实例,确保遇到Nothing时能自动生成空的默认结构:
instance Monoid RecB where mempty = RecB Nothing Nothing -- 合并逻辑:保留已有非Nothing的值,避免被空值覆盖 mappend (RecB b1a b2a) (RecB b1b b2b) = RecB (b1a <|> b1b) (b2a <|> b2b) instance Monoid RecA where mempty = RecA Nothing Nothing mappend (RecA a1a a2a) (RecA a1b a2b) = RecA (a1a <|> a1b) (a2a <> a2b)
4. 实现深度嵌套更新
利用lens库的non组合子,穿透所有Maybe层完成更新,无论父级是Nothing还是Just:
let myVal = Nothing :: Maybe RecA updatedVal = set (non mempty . aField2 . non mempty . bField1) (Just "ValueFor_bField1") myVal
关键说明
non mempty:lens提供的棱镜工具,能将Maybe a类型转换为a处理——原类型是Nothing时用mempty(我们定义的空结构)替代,是Just x时直接使用x。- 组合路径
non mempty . aField2 . non mempty . bField1:依次穿透Maybe RecA→RecA的aField2(Maybe RecB)→RecB的bField1(Maybe Text),自动处理所有Nothing层,创建必要的默认结构后设置目标值。 - 最终
updatedVal会生成Just (RecA Nothing (Just (RecB (Just "ValueFor_bField1") Nothing))),完全满足需求。
手动定义Lens的替代方案
如果不想使用TemplateHaskell,可以手动编写Lens:
aField2 :: Lens' RecA (Maybe RecB) aField2 = lens _aField2 (\rec newVal -> rec { _aField2 = newVal }) bField1 :: Lens' RecB (Maybe Text) bField1 = lens _bField1 (\rec newVal -> rec { _bField1 = newVal })
内容的提问来源于stack exchange,提问作者Saurabh Nanda
相关产品推荐
相关产品推荐

