gpt4 book ai didi

haskell - Haskell 的记录语法有什么有用的抽象吗?

转载 作者:行者123 更新时间:2023-12-04 14:44:22 25 4
gpt4 key购买 nike

为了尝试简化这个问题,我定义了这些箭头函数:

splitA :: (Arrow arr) => arr a b -> arr a (b,a)
splitA ar = ar &&& (arr (\a -> id a))

recordArrow
:: (Arrow arr)
=> (d -> r)
-> (d -> r -> d)
-> (r -> r)
-> arr d d
recordArrow g s f = splitA (arr g >>^ f) >>^ \(r,d) -> s d r

然后让我做这样的事情:
unarrow :: ((->) b c) -> (b -> c) -- unneeded as pointed out to me in the comments
unarrow g = g


data Testdata = Testdata { record1::Int,record2::Int,record3::Int }

testRecord = unarrow $
recordArrow record1 (\d r -> d { record1 = r }) id
>>> recordArrow record2 (\d r -> d { record2 = r }) id
>>> recordArrow record3 (\d r -> d { record3 = r }) id

如您所见,这并没有很好地利用 DRY。

我希望可能有某种语言扩展可以帮助简化这个过程。所以上面可以简单地重写为:
testRecord' = unarrow $
recordArrow' record1 id
>>> recordArrow' record2 id
>>> recordArrow' record3 id

为清楚起见已更新 : 稍微澄清一下。我知道我可以做这样的事情:
foo d = d { record1 = id (record1 d), record2 = id (record2 d) }

但这忽略了任何执行顺序和任何状态。假设 record2 的更新函数依赖于 record1 的更新值.或者,我可能想要创建一个不同的箭头,如下所示: arr d (d,x)然后我想建立一个列表 [x]其顺序取决于记录的评估顺序。

我发现我经常想执行一些功能,然后更新记录。我可以通过像这样线程化状态来做到这一点
g :: d -> r -> d
foo d = let d' = d { record1 = (g d) (record1 d) } in d' { record2 = (g d') (record2 d') }

但我认为箭头符号更整洁,我也可以有 [arr d d]并将它们按顺序链接在一起。加如果 rd是 Monad,它创建了更整洁的代码。或者,如果它们都是 Monad,让我执行分层绑定(bind),而无需使用 Monad Transformer。在 ST s x 的情况下它让我线程状态 s有条不紊地绕来绕去。

我不是要解决一个特定的问题。我只是想找到一种更新记录语法的抽象方法,而不必明确定义某种“getter”和“setter”。

以下在评论中得到了回答——
旁注:我必须定义一个函数 unarrow用于将函数 (->) 箭头转换回函数。否则,如果我有 someArrow b箭头 arr b c我无法估价 c .与 unarrow我可以写的函数 unarrow someArrow b它工作正常。我觉得我在这里一定做错了,因为我对 unarrow 的定义很简单 unarrow g = g .

最佳答案

您正在寻找的抽象称为镜头和 lens Hackage 上的 package 可能是目前使用的这个想法最广泛的实现。使用 lens包你可以定义你的recordArrow'作为

{-# LANGUAGE RankNTypes #-}

import Control.Arrow
import Control.Lens

recordArrow' :: Arrow arr => Lens' d r -> (r -> r) -> arr d d
recordArrow' field f = arr $ field %~ f

%~ 是一个更新运算符,它通过给定的镜头使用函数更新更大数据结构中的值。

现在的问题是您不会自动为记录字段获取镜头,但您可以手动定义它们或使用 Template Haskell 自动生成它们。例如
{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

data Testdata = Testdata { _record1::Int, _record2::Int, _record3::Int }

makeLenses ''Testdata

请注意,原始记录访问器带有下划线前缀,以便我们可以使用镜头的原始名称。
testRecord :: Testdata -> Testdata
testRecord =
recordArrow' record1 id
>>> recordArrow' record2 id
>>> recordArrow' record3 id
unarrow不需要功能。最好通过简单地给出类型签名来将泛型类型强制为具体类型。

请注意,如果您只是在寻找一种更好的语法来链接记录操作并且没有任何其他箭头的用途,您可能更喜欢只使用 State带镜头的单子(monad)。例如:
import Control.Monad.State (execState)

testRecord' :: Testdata -> Testdata
testRecord' = execState $ do
record1 .= 3
record2 %= (+5)
record3 += 2

关于haskell - Haskell 的记录语法有什么有用的抽象吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25954467/

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