简体   繁体   English

Haskell是否有记录成员的指针/引用?

[英]Does Haskell have pointers/references to record members?

I can create and reference relative pointers to struct members in C++ using the ::* , .* , and ->* syntax like : 我可以使用::*.*->*语法创建和引用C ++中结构成员的相对指针,如:

char* fstab_t::*field = &fstab_t::fs_vfstype;
my_fstab.*field = ...

In Haskell, I can easily create temporary labels for record getters like : 在Haskell中,我可以轻松地为记录getter创建临时标签,例如:

(idxF_s,idxL_s) = swap_by_sign sgn (idxF,idxL) ;

Afaik, I cannot however then update records using these getters as labels like : Afaik,然后我不能使用这些getter更新记录,如:

a { idxF_s = idxL_s b }

Is there an easy way to do this without coding for each record setter? 是否有一种简单的方法可以在不编码每个记录设置器的情况下执行此操作

A getter and setter bundled together in a first-class value is referred to as a lens . 将第一类值捆绑在一起的吸气剂和定位器称为透镜 There are quite a few packages for doing this; 这样做有很多套餐; the most popular are data-lens and fclabels . 最受欢迎的是数据镜头fclabels This previous SO question is a good introduction. 以前的SO问题是一个很好的介绍。

Both of those libraries support deriving lenses from record definitions using Template Haskell (with data-lens, it's provided as an additional package for portability). 这两个库都支持使用Template Haskell从记录定义中导出镜头(使用数据镜头,它作为可移植性的附加包提供)。 Your example would be expressed as (using data-lens syntax): 您的示例将表示为(使用数据镜头语法):

setL idxF_s (b ^. idL_s) a

(or equivalently: idxF_s ^= (b ^. idL_s) $ a ) (或等效地: idxF_s ^= (b ^. idL_s) $ a

You can, of course, transform lenses in a generic way by transforming their getter and setter together: 当然,你可以通过将他们的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

(or equivalently: negateLens l = iso negate negate . l 1 ) (或等效地: negateLens l = iso negate negate . l 1

In general, I would recommend using lenses whenever you have to deal with any kind of non-trivial record handling; 一般来说,我建议您在处理任何类型的非平凡记录处理时使用镜头; not only do they vastly simplify pure transformation of records, but both packages contain convenience functions for accessing and modifying a state monad's state using lenses, which is incredibly useful. 它们不仅极大地简化了记录的纯转换,而且两个包都包含使用镜头访问和修改状态monad状态的便利功能,这非常有用。 (For data-lens, you'll want to use the data-lens-fd package to use these convenience functions in any MonadState ; again, they're in a separate package for portability.) (对于数据镜头,您需要使用data-lens-fd软件包在任何MonadState使用这些便捷功能;同样,它们在一个单独的软件包中以便于移植。)


1 When using either package, you should start your modules with: 1使用任一软件包时,您应该使用以下命令启动模块:

import Prelude hiding (id, (.))
import Control.Category

This is because they use generalised forms of the Prelude's id and (.) functions — id can be used as the lens from any value to itself (not all that useful, admittedly), and (.) is used to compose lenses (eg getL (fieldA . fieldB) a is the same as getL fieldA . getL fieldB $ a ). 这是因为它们使用Prelude的id(.)函数的通用形式 - id可以用作从任何值到自身的镜头(不是所有有用的,不可否认),并且(.)用于构成镜头(例如getL (fieldA . fieldB) agetL fieldA . getL fieldB $ a相同getL fieldA . getL fieldB $ a )。 The shorter negateLens definition uses this. 较短的negateLens定义使用它。

What you want here is first-class record labels, and while this does not exist in the language, there are several packages on Hackage which implement this pattern. 你想要的是一流的唱片公司标签,虽然这种语言不存在,但Hackage上有几个实现这种模式的软件包。 One of these is fclabels , which can use Template Haskell to generate the required boilerplate for you. 其中一个是fclabels ,它可以使用Template Haskell为您生成所需的样板。 Here's an example: 这是一个例子:

{-# LANGUAGE TemplateHaskell #-}

import Control.Category
import Data.Label
import Prelude hiding ((.))

data Foo = Foo { _fieldA :: Int, _fieldB :: Int }
  deriving (Show)

$(mkLabels [''Foo])

main = do
  let foo = Foo 2 3

  putStrLn "Pick a field, A or B"
  line <- getLine

  let field = (if line == "A" then fieldA else fieldB)

  print $ modify field (*10) foo

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

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