我会对van Laarhoven的isomorphism lenses的一个小例子感兴趣,它适用于像data BValue = BValue { π :: Float, σ :: Float, α :: Float } deriving Show
这样的数据类型(具体来说,是get / set / modify函数)。提前谢谢。
答案 0 :(得分:6)
来自van Laarhoven的帖子,Lens
类型是
data Lens a b = forall r . Lens (Iso a (b, r))
所以在我们的例子中a
是BValue
,我们想构建一些选择一个或多个元素的leneses。例如,让我们构建一个可以选择π的镜头。
piLens :: Lens BValue Float
所以这将是从BValue
到Float
的镜头(即三联中的第一个镜头,标签为pi。)
piLens = Lens (Iso {fw = piFwd, bw = piBwd})
镜头选出两件事:残差类型r
(这里省略,因为我们不必在haskell中明确指定存在类型),以及同构。同构又由前向和后向函数组成。
piFwd :: BValue -> (Float, (Float, Float))
piFwd (BValue {pi, sigma, alpha}) = (pi, (sigma, alpha))
正向功能只是隔离了我们想要的组件。请注意,我的残差类型是“值的其余部分”,即一对sigma和alpha浮点数。
piBwd :: (Float, (Float, Float)) -> BValue
piBwd (pi, (sigma, alpha)) = BValue { pi = pi, sigma = sigma, alpha = alpha }
向后功能类似。
所以现在我们已经定义了一个镜头来操纵BValue
的π组件。
其他七个镜头相似。 (7个镜头:选择sigma和alpha,挑出所有可能的对(忽略顺序),挑出所有BValue
并选择()
)。
我不确定的一点是严格:我有点担心我写的fw和bw函数太严格了。不确定。
我们还没有完成:
我们仍然需要检查piLens
是否真的尊重镜头规律。 van Laarhoven对Lens
的定义的好处在于我们只需要检查同构定律;透镜法则通过他博客文章中的计算得出。
所以我们的证明义务是:
fw piLens . bw piLens = id
bw piLens . fw piLens = id
这两个证据都直接来自piFwd
和piBwd
的定义以及有关构图的法律。
答案 1 :(得分:2)
查看Data.Label from the fclabels package,它实现了记录类型的镜头。
为了说明这个包,让我们采用以下两个示例数据类型。
import Data.Label import Prelude hiding ((.), id) data Person = Person { _name :: String , _age :: Int , _isMale :: Bool , _place :: Place } data Place = Place { _city , _country , _continent :: String }
两种数据类型都是记录类型,所有标签都以下划线为前缀。这个下划线表示我们的Template Haskell代码可以为这些字段派生镜头。使用这种简单的单线程可以完成透镜的衍生:
$(mkLabels [''Person, ''Place])
对于所有标签,将创建镜头。
现在让我们来看看这个例子。这个71岁的老人,我的邻居叫Jan,并不介意以他为例:
jan :: Person jan = Person "Jan" 71 True (Place "Utrecht" "The Netherlands" "Europe")
当我们想确定Jan真的和他声称的那样古老时,我们可以使用get函数将年龄作为整数来表示:
hisAge :: Int hisAge = get age jan
考虑一下他现在想搬到阿姆斯特丹:有什么更好的地方度过你过去的日子。使用组合我们可以更改结构内部的城市价值:
moveToAmsterdam :: Person -> Person moveToAmsterdam = set (city . place) "Amsterdam"
现在:
ghci> moveToAmsterdam jan Person "Jan" 71 True (Place "Amsterdam" "The Netherlands" "Europe")
使用(。)运算符完成组合,该运算符是Control.Category模块的一部分。确保导入此模块并隐藏Haskell Prelude中的默认(。),id函数。