Haskell 是否提供了一种将函数映射到数据成员的方法?
Does Haskell provide a way to map a function to a data member?
我是一名 Haskell 菜鸟,我经常发现自己不得不使用模式匹配分解数据,只是为了将函数应用于其成员之一,然后重新组合它。
假设我有:
data Car = Car { gas :: Int, licensePlate :: String }
我希望它在行驶时将汽油减半,然后加油,我正在做:
mapGas:: (Int -> Int) -> Car -> Car
mapGas f (Car aGas aLicensePlate) = Car (f aGas) aLicensePlate
drive:: Car -> Car
drive = mapGas (flip div 2)
refuel:: Int -> Car -> Car
refuel = mapGas . (+)
有没有一种方法可以做到这一点而不必定义辅助函数 mapGas?因为当数据由许多字段组成时,必须为数据的每个成员编写映射函数会变得相当麻烦。我知道可以使用访问器为其中一个成员赋值:
runOutOfFuel:: Car -> Car
runOutOfFuel aCar = aCar { gas = 0 }
是否也可以使用访问器映射函数?如果可以,怎么做?
没有语言功能可以做到这一点,但是,与Haskell中的许多事情一样,核心语言足够强大,可以在简单而优雅的方式。
您正在寻找的解决方案是一种称为 lens 的值。镜头完全可以满足您的需求:它允许您获取任何类型的抽象数据并对其一部分应用函数,从而获得包含修改部分的整个数据值。
有镜头介绍我很喜欢here. To use the examples included, you'll need the lens
package. (Or this one if you're using Stack)
只使用核心库?不,但是对于广泛使用的 lens
包,是的。以下是您的情况:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens.TH
import Control.Lens
data Car = Car { _gas :: Int, _licensePlate :: String }
makeLenses ''Car
现在,您可以轻松地 get/set/modify 嵌套在数据结构中的字段。
runOutOfFuel:: Car -> Car
runOutOfFuel = gas .~ 0
drive:: Car -> Car
drive = gas %~ (`div` 2)
refuel:: Int -> Car -> Car
refuel c = gas +~ c
这里的神奇之处在于 makeLenses ''Car
生成 gas
和 licensePlate
与您的 mapGas
相似(但更强大)的函数(事实上,mapGas = (gas %~)
). lens
入门非常令人生畏,但我建议您只阅读 examples 部分。
我是一名 Haskell 菜鸟,我经常发现自己不得不使用模式匹配分解数据,只是为了将函数应用于其成员之一,然后重新组合它。
假设我有:
data Car = Car { gas :: Int, licensePlate :: String }
我希望它在行驶时将汽油减半,然后加油,我正在做:
mapGas:: (Int -> Int) -> Car -> Car
mapGas f (Car aGas aLicensePlate) = Car (f aGas) aLicensePlate
drive:: Car -> Car
drive = mapGas (flip div 2)
refuel:: Int -> Car -> Car
refuel = mapGas . (+)
有没有一种方法可以做到这一点而不必定义辅助函数 mapGas?因为当数据由许多字段组成时,必须为数据的每个成员编写映射函数会变得相当麻烦。我知道可以使用访问器为其中一个成员赋值:
runOutOfFuel:: Car -> Car
runOutOfFuel aCar = aCar { gas = 0 }
是否也可以使用访问器映射函数?如果可以,怎么做?
没有语言功能可以做到这一点,但是,与Haskell中的许多事情一样,核心语言足够强大,可以在简单而优雅的方式。
您正在寻找的解决方案是一种称为 lens 的值。镜头完全可以满足您的需求:它允许您获取任何类型的抽象数据并对其一部分应用函数,从而获得包含修改部分的整个数据值。
有镜头介绍我很喜欢here. To use the examples included, you'll need the lens
package. (Or this one if you're using Stack)
只使用核心库?不,但是对于广泛使用的 lens
包,是的。以下是您的情况:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens.TH
import Control.Lens
data Car = Car { _gas :: Int, _licensePlate :: String }
makeLenses ''Car
现在,您可以轻松地 get/set/modify 嵌套在数据结构中的字段。
runOutOfFuel:: Car -> Car
runOutOfFuel = gas .~ 0
drive:: Car -> Car
drive = gas %~ (`div` 2)
refuel:: Int -> Car -> Car
refuel c = gas +~ c
这里的神奇之处在于 makeLenses ''Car
生成 gas
和 licensePlate
与您的 mapGas
相似(但更强大)的函数(事实上,mapGas = (gas %~)
). lens
入门非常令人生畏,但我建议您只阅读 examples 部分。