gpt4 book ai didi

haskell - 有没有办法在类型中表达记录的读/写/读写属性?

转载 作者:行者123 更新时间:2023-12-02 18:41:28 25 4
gpt4 key购买 nike

假设您有以下产品类型:

data D = D { getA :: Int, getB :: Char, getC :: [Double] }

假设你有一个函数:

f :: D -> D

仅读取getA字段,但修改getBgetC

是否有一种方便的方法可以用 f 类型来表达这一点?

最佳答案

那么,让我们考虑一个例子:

f :: D -> D
f d = d { getC = map (+ fromIntegral (getA d)) (getC d) }

显然,一旦您拥有像 D -> D 这样的具体类型,所有保证都会消失:可以想象,该函数可以用它的参数执行任何事情

如果你想防止这种情况发生,你需要将具体的 D 替换为抽象的,例如

f :: d -> d

但是,当然,实现将不再起作用,因为在 d 上,您无能为力。

   • Couldn't match expected type ‘d’ with actual type ‘D’
‘d’ is a rigid type variable bound by
the type signature for:
f :: forall d. d -> d

要重新启用您想要的特定操作,您可以将它们作为参数传递。那么,什么是“读取操作或修改操作参数”?
输入lenses 。让我们首先使用它们重写所有原始示例:

{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data D = D { _getA :: Int, _getB :: Char, _getC :: [Double] }
makeLenses ''D

f :: D -> D
f d = d & getC %~ map (+ fromIntegral (d^.getA))

现在,通过使 d 抽象,但将必要的访问操作作为参数传递,可以很容易地概括/强化:

type AGetter' s a = Getting a s a   -- for some reason this isn't defined
-- in the `lens` library

f' :: AGetter' d Int -> ASetter' d [Double] -> d -> d
f' getInt setDbls d = d & setDbls %~ map (+ fromIntegral (d^.getInt))

它允许您通过传递 getAgetC 镜头来获取旧行为:

f :: D -> D
f = f' getA getC

此方法有效的原因是 lens 使用类型类/通用量化类型技巧来编码子类型关系:getA 具有类型 Lens' D Int,但 AGetter' D Int 是其功能缩减的父类(super class)型,从而保证您实际上只读取焦点元素,而不读取其他元素。

<小时/>

技术细节:您已经注意到我编写了 ASetter',而不是 Setter'ASetter。这意味着什么:

  • OᴘᴛɪᴄAnOᴘᴛɪᴄ 版本是其rank-0 对应者。所以例如ALens 只能用作镜头,而不是用作镜头。 getter,而 Lens 可以用作 getter 或 setter、遍历或折叠。
    将函数参数限制为具体的 AnOᴘᴛɪᴄ 版本被认为是很好的风格,因为这意味着编译器实际上不必处理 2 级类型。 (Lens 本身的类型只是 1 级多态,但将其作为参数传递将使接受函数成为 2 级多态。)
  • OᴘᴛɪᴄOᴘᴛɪᴄ' 版本是非类型更改变体。原则上,例如setter 还可以更改它关注的字段的类型 - 例如当您将 (Bool, Char) 元组的 snd 类型更改为 String 时,这将是一个 Setter (Bool,Char ) (Bool,String) Char String,但如果您只是将第二个字段更改为另一个 Char,它只是一个 Setter' (Bool,Char) Char (实际上是a synonym,用于恰好更改为相同类型的类型更改 setter )。

关于haskell - 有没有办法在类型中表达记录的读/写/读写属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59874924/

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