Haskell:根据变量替换记录中的字段

标签 haskell types

举个例子,我有一辆自行车,有前轮和后轮,它们都有一个 Int 来表示直径。

type Wheel = Int
data Bike = Bike { frontwheel :: Wheel, rearwheel :: Wheel }
    deriving (Show)

mybike = Bike 24 26

现在我想更换一个轮子,因为我不喜欢它们大小不同:

replaceFrontWheel :: Bike -> Wheel -> Bike
replaceFrontWheel bike wheel = bike { frontwheel = wheel }

repairedbike = replaceFrontWheel mybike 26 

有效!

但是,如果我想要一个可以替代前轮后轮的功能怎么办?毕竟两个轮子都是 Wheel (Int) 类型,那么为什么不使用一个将字段也作为参数的函数来实现:

replaceWheel bike position wheel = bike { position = wheel }

repairedbike = replaceWheel mybike frontwheel 26 

我明白为什么那行不通。 position 未被解释为具有值 frontwheel,而是被解释为 Bike 的(不存在的)字段 position >.

是否有 (JS) mybike[position] = 26 或 (PHP) $mybike->$position = 26 的 Haskell 模拟?

是否可以在没有任何外部模块的情况下以优雅的方式实现?

不然的话,用镜头有没有可能?

最佳答案

是的,lens es 正是您所需要的。

import Control.Lens
import Control.Lens.TH

data Bike = Bike { _frontwheel, _rearwheel :: Wheel }
deriving (Show)
makeLenses ''Bike

replaceWheel :: Bike -> Lens' Bike Wheel -> Wheel -> Bike
replaceWheel bike position wheel = bike & position .~ wheel

随心所欲地使用:

repairedbike = replaceWheel mybike frontwheel 26

你可以稍微弱化签名:

replaceWheel :: Bike -> Setter' Bike Wheel -> Wheel -> Bike

这本质上只是一种奇特的说法

replaceWheel :: Bike
             -> ((Wheel->Identity Wheel) -> (Bike->Identity Bike))
             -> Wheel
             -> Bike

因为 Identity 只是一个类型级别的同构,你还不如省略它,最终得到

replaceWheel :: Bike -> ((Wheel->Wheel) -> Bike->Bike) -> Wheel -> Bike
replaceWheel bike position wheel = bike & position (const wheel)
                              -- = position (const wheel) bike

可以这样调用:

data Bike = Bike { _frontwheel, _rearwheel :: Wheel } -- no lenses

frontWheel :: (Wheel -> Wheel) -> Bike -> Bike
frontWheel f (Bike fw rw) = Bike (f fw) rw

repairedbike = replaceWheel mybike frontwheel 26

所以,严格来说,您确实不需要为此使用任何库!

最好使用合适的镜头而不是这种临时近似的原因包括:

  • 更一般。 Lens' 可用于设置、获取(和遍历)值。如果没有 lens 使用的底层 Rank2 多态性,这只能笨拙地表达。
  • 更简洁。上面的类型有很多冗余; lens 为您提供了这些访问器的简短同义词。
  • 更安全。函数 (Wheel -> Wheel) -> Bike -> Bike 可以处理各种垃圾; lens 需要 lens 法则,该法则基本上保证了 lens 实际上像唱片访问器(accessor)一样工作,仅此而已。
  • 快。 lens 库中的组合器在编写时考虑了性能(即支持流融合的内联,省略状态 monad 中的复制等)。

顺便说一句,对于“修改某些东西”的函数,在 Haskell 中通常将要修改的参数放在最后:

replaceWheel :: Setter' Bike Wheel -> Wheel -> Bike -> Bike
replaceWheel position wheel = position .~ wheel

...或者,更短,

replaceWheel = (.~)

关于Haskell:根据变量替换记录中的字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39551523/

相关文章:

C 在其他元素中存储数组元素地址

java - java中number/10和number*0.1的区别

haskell - ixmap、数组和逆变仿函数之间的关系是什么?

haskell - 我无法确定为什么我收到 ***Exception : Stack overflow

Haskell 无法从上下文中推导出 (t ~ t1) (框架 t)

列表类型错误的 Haskell 列表

Haskell - 这个素性测试中的 lambda 是什么意思以及它是如何工作的?

haskell - 如何连接两个 Haskell IO monad

haskell - 共享与非共享定点组合器

typescript - 在 Typescript 中解构函数参数