我可以使用::*
,.*
和->*
语法在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 }
有没有一种简单的方法可以在不编码每个记录设置器的情况下执行此操作?
答案 0 :(得分:10)
将第一类值捆绑在一起的getter和setter称为镜头。这样做有很多套餐;最受欢迎的是data-lens和fclabels。这个previous SO question是一个很好的介绍。
这两个库都支持使用Template Haskell从记录定义中导出镜头(使用数据镜头,它提供为an additional package以便于移植)。您的示例将表示为(使用数据镜头语法):
setL idxF_s (b ^. idL_s) a
(或等效地:idxF_s ^= (b ^. idL_s) $ a
)
当然,你可以通过将他们的吸气剂和二传手变换在一起来以一般方式转换镜头:
-- 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 . l
1 )
一般情况下,我建议您在处理任何类型的非平凡记录处理时使用镜头;它们不仅极大地简化了记录的纯转换,而且两个包都包含使用镜头访问和修改状态monad状态的便利功能,这非常有用。 (对于数据镜头,您需要使用data-lens-fd包在任何MonadState
中使用这些便利功能;同样,它们位于单独的包中以便于移植。)
1 使用任一软件包时,应使用以下命令启动模块:
import Prelude hiding (id, (.))
import Control.Category
这是因为他们使用Prelude的id
和(.)
函数的广义形式 - id
可以用作从任何值到自身的镜头(不是所有有用的,不可否认) ,(.)
用于构成镜头(例如getL (fieldA . fieldB) a
与getL fieldA . getL fieldB $ a
相同)。较短的negateLens
定义使用此。
答案 1 :(得分:5)
你想要的是一流的唱片公司标签,虽然这种语言不存在,但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