繁体   English   中英

同构镜头

[英]Isomorphism lenses

我会对van Laarhoven的同构镜头的一个小例子感兴趣,应用于数据类型如data BValue = BValue { π :: Float, σ :: Float, α :: Float } deriving Show (具体来说,get / set /修改功能)。 先感谢您。

从van Laarhoven的帖子来看, Lens类型是

data Lens a b = forall r . Lens (Iso a (b, r))

所以在我们的例子中, aBValue ,我们想构建一些选择一个或多个元素的leneses。 例如,让我们构建一个能够挑出π的镜头。

piLens :: Lens BValue Float

所以它将是一个从BValueFloat的镜头(即三重中的第一个镜头,带有标签pi。)

piLens = Lens (Iso {fw = piFwd, bw = piBwd})

镜头选择了两个东西:残差类型r (这里省略,因为我们不必在haskell中明确指定存在类型),以及同构。 同构又由前向和后向函数组成。

piFwd :: BValue ->  (Float, (Float, Float))
piFwd (BValue {pi, sigma, alpha}) = (pi, (sigma, alpha))

forward函数只是隔离了我们想要的组件。 请注意,我的剩余类型是“值的其余部分”,即一对sigma和alpha浮点数。

piBwd :: (Float, (Float, Float)) -> BValue
piBwd (pi, (sigma, alpha)) = BValue { pi = pi, sigma = sigma, alpha = alpha }

后向功能类似。

所以现在我们已经定义了一个镜头来操纵BValue的pi组件。

其他七个镜头是相似的。 (7个镜头:选择西格玛和阿尔法,挑出所有可能的对(无视顺序),挑出所有的BValue并选择() )。

我不确定的一点是严格:我有点担心我写的fw和bw函数太严格了。 不确定。

我们还没有完成:

我们仍然需要检查piLens真的尊重镜头规律。 van Laarhoven对Lens定义的好处在于我们只需要检查同构定律; 透镜法则通过他博客文章中的计算得出。

所以我们的证明义务是:

  1. fw piLens . bw piLens = id
  2. bw piLens . fw piLens = id

这两个证据都直接遵循piFwdpiBwd的定义以及关于组成的定律。

从fclabels包中查看Data.Label ,它实现了记录类型的镜头。

为了说明这个包,让我们采用以下两个示例数据类型。

 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函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM