同构镜头

时间:2011-08-29 17:30:04

标签: haskell

我会对van Laarhoven的isomorphism lenses的一个小例子感兴趣,它适用于像data BValue = BValue { π :: Float, σ :: Float, α :: Float } deriving Show这样的数据类型(具体来说,是get / set / modify函数)。提前谢谢。

2 个答案:

答案 0 :(得分:6)

来自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))

正向功能只是隔离了我们想要的组件。请注意,我的残差类型是“值的其余部分”,即一对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的定义的好处在于我们只需要检查同构定律;透镜法则通过他博客文章中的计算得出。

所以我们的证明义务是:

  1. fw piLens . bw piLens = id
  2. bw piLens . fw piLens = id
  3. 这两个证据都直接来自piFwdpiBwd的定义以及有关构图的法律。

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