【问题标题】:Does Haskell have pointers/references to record members?Haskell 是否有指向记录成员的指针/引用?
【发布时间】:2012-02-26 23:28:45
【问题描述】:

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

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

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

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

Afaik,但是我不能使用这些 getter 作为标签来更新记录,例如:

a { idxF_s = idxL_s b }

有没有一种简单的方法可以在不为每个记录设置器编码的情况下做到这一点?

【问题讨论】:

  • 指向成员运算符的指针不是 C 而是 C++。重新标记。

标签: haskell template-haskell member-pointers


【解决方案1】:

将getter 和setter 捆绑在一个一流的值中称为lens。有很多包可以做到这一点;最受欢迎的是data-lensfclabels。这个previous SO question 是一个很好的介绍。

这两个库都支持使用 Template Haskell 从记录定义派生镜头(对于数据镜头,它以an additional package 的形式提供以方便移植)。您的示例将表示为(使用 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 定义使用了这个。

【讨论】:

    【解决方案2】:

    你想要的是一流的唱片公司,虽然这在语言中不存在,但 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-24
      • 1970-01-01
      • 1970-01-01
      • 2013-05-02
      • 1970-01-01
      • 2023-03-27
      • 2023-03-12
      相关资源
      最近更新 更多