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

发布于 2025-01-02 11:41:37 字数 442 浏览 5 评论 0原文

我可以使用 ::*.*->* 语法创建和引用 C++ 中结构成员的相对指针,例如:

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

在Haskell中,我可以轻松地为记录获取器创建临时标签,例如:

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

Afaik,但是我无法使用这些获取器作为标签来更新记录,例如:

a { idxF_s = idxL_s b }

是否有一种简单的方法可以做到这一点,而无需为每个记录设置器进行编码?

I can create and reference relative pointers to struct members in C++ using the ::*, .*, and ->* syntax like :

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

In Haskell, I can easily create temporary labels for record getters like :

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

Afaik, I cannot however then update records using these getters as labels like :

a { idxF_s = idxL_s b }

Is there an easy way to do this without coding for each record setter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

听闻余生 2025-01-09 11:41:37

捆绑在一起的一等值中的 getter 和 setter 称为透镜。有很多包可以做到这一点;最流行的是 data-lensfclabels。这个上一个问题是很好的介绍。

这两个库都支持使用 Template Haskell 从记录定义中派生镜头(使用 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 .l1

一般来说,我建议您在任何时候都使用镜头必须处理任何类型的重要记录处理;它们不仅极大地简化了记录的纯粹转换,而且两个包都包含使用镜头访问和修改状态单子状态的便利函数,这非常有用。 (对于 data-lens,您需要使用 data-lens-fd 包以在任何 MonadState 中使用这些便利函数;同样,为了可移植性,它们位于单独的包中。)


1 当使用任一包时,您应该启动您的模块与:

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

这是因为他们使用 Prelude 的 id(.) 函数的通用形式 - id 可以用作从任何值到自身的镜头(并非所有值)无可否认,这很有用),并且 (.) 用于组成镜头(例如 getL (fieldA . fieldB) agetL fieldA . getL fieldB 相同$a)。较短的 negateLens 定义使用了它。

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. This previous SO question is a good introduction.

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). 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)

You can, of course, transform lenses in a generic way by transforming their getter and setter together:

-- 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 . l1)

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. (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.)


1 When using either package, you should start your modules with:

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 (e.g. getL (fieldA . fieldB) a is the same as getL fieldA . getL fieldB $ a). The shorter negateLens definition uses this.

伤痕我心 2025-01-09 11:41:37

这里你想要的是一流的记录标签,虽然这在语言中不存在,但 Hackage 上有几个包实现了这种模式。其中之一是 fclabels,它可以使用 Template Haskell 为您生成所需的样板。这是一个例子:

{-# 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

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. One of these is fclabels, which can use Template Haskell to generate the required boilerplate for you. 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文