gpt4 book ai didi

haskell - 如何简洁地修改记录的字段?

转载 作者:行者123 更新时间:2023-12-04 16:50:44 24 4
gpt4 key购买 nike

data Person = Person
{
name :: String
, counter :: Int
}

incrementPersonCounter :: Person -> Person
incrementPersonCounter p@(Person _ c) = p { counter = c + 1 }

有没有更简洁的方法来做上述事情?是否有我可以使用的函数,我可以在其中指定记录、其字段之一(在本例中为 name/ counter)和一个应用于返回值的函数?

我在想一些事情:
applyRecord r f f' = r
{ f = f' (f r) }

虽然这行不通,因为:
error: Not in scope: ‘f’
|
13 | { f = f' (f r) }

最佳答案

一种概括方法 incrementPersonCounter是对修改函数进行抽象:

modifyPersonCounter :: (Int -> Int) -> Person -> Person
modifyPersonCounter f p = (\c -> p { counter = c}) $ f (counter p)

事实上,一个常见的模式是对我们想要在现场执行的效果进行抽象:
counterLens :: forall f. Functor f => (Int -> f Int) -> (Person -> f Person)
counterLens f p = (\c -> p { counter = c }) <$> f (counter p)

例如,我们可能想从控制台或数据库( IO 效果)读取计数器增加。

我们可以给一个 synonym给定一种(可能有效的)改变字段的方法,返回一个转换整个记录的函数的函数类型:
type Lens' a b = forall f. Functor f => (b -> f b) -> (a -> f a)

为了纯粹修改记录,现在我们需要一个辅助函数,我们可以调用 over ,只需要定义一次:
over :: Lens' a b -> (b -> b) -> a -> a
over l f p = runIdentity $ l (Identity . f) p

例如:
*Main> over counterLens (+1) (Person "foo" 40)
Person {name = "foo", counter = 41}

我们已经抽象了修改函数及其可能的效果,但是我们仍然需要为每个字段定义这些“镜头”,这很烦人。在实践中,人们使用 Template Haskell自动定义它们并避免样板。

但是如果我们想要一个让我们指定字段名称的函数呢?可悲的是,这更复杂。您需要一种将类型级字符串作为参数传递的方法,以及一个 multi-parameter type class对字段名称、记录类型和字段类型之间的关系进行编码。有 some packages这样做(同样,模板 Haskell 帮助样板)但据我所知,它们并没有被广泛使用。

镜头的主库称为 lens ,还有 microlens ,具有更轻依赖足迹的替代方案。它们是可互操作的:使用一个库定义的镜头与另一个库一起使用。

关于haskell - 如何简洁地修改记录的字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47728980/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com