以第一类值捆绑在一起的 getter 和 setter 称为lens。有很多包可以做到这一点;最受欢迎的是data-lens和fclabels。这个以前的 SO question是一个很好的介绍。
这两个库都支持使用 Template Haskell 从记录定义派生镜头(对于 data-lens,它是作为可移植性的附加包提供的)。您的示例将表示为(使用 data-lens 语法):
setL idxF_s (b ^. idL_s) a
(或等效地idxF_s ^= (b ^. idL_s) $ a
:)
当然,您可以通过将 getter 和 setter 一起转换来以通用方式转换镜头:
-- I don't know what swap_by_sign is supposed to do.
negateLens :: (Num b) => Lens a b -> Lens a b
negateLens l = lens get set
where
get = negate . getL l
set = setL l . negate
(或等效地:negateLens l = iso negate negate . l
1)
一般而言,我建议您在需要处理任何重要的记录处理时使用镜头;它们不仅极大地简化了记录的纯转换,而且两个包都包含使用镜头访问和修改状态单子状态的便利功能,这非常有用。(对于 data-lens,您需要使用data-lens-fd包在 any 中使用这些便利功能MonadState
;同样,为了便于移植,它们位于单独的包中。)
1使用任一包时,您应该使用以下命令开始您的模块:
import Prelude hiding (id, (.))
import Control.Category
id
这是因为它们使用了 Prelude和(.)
函数的广义形式—id
可以用作从任何值到自身的镜头(诚然,不是所有有用的),并(.)
用于组合镜头(例如getL (fieldA . fieldB) a
,与 相同getL fieldA . getL fieldB $ a
)。较短的negateLens
定义使用这个。