作为一个例子,我有一个带前后轮的自行车,它们都只有一个Int来代表直径.
type Wheel = Int
data Bike = Bike { frontwheel :: Wheel, rearwheel :: Wheel }
deriving (Show)
mybike = Bike 24 26
Run Code Online (Sandbox Code Playgroud)
现在我想更换一个轮子,因为我不喜欢它们的尺寸不同:
replaceFrontWheel :: Bike -> Wheel -> Bike
replaceFrontWheel bike wheel = bike { frontwheel = wheel }
repairedbike = replaceFrontWheel mybike 26
Run Code Online (Sandbox Code Playgroud)
这样可行!
但是,如果我想要一个可以替换前轮或后轮的功能呢?毕竟两个轮子都是Wheel(Int)类型,所以为什么不使用单个函数来实现它,该函数也将该字段作为参数:
replaceWheel bike position wheel = bike { position = wheel }
repairedbike = replaceWheel mybike frontwheel 26
Run Code Online (Sandbox Code Playgroud)
我明白为什么这不起作用.position不被解释为具有值frontwheel,但作为(不存在)字段position的Bike.
是否有(JS)mybike[position] = 26或(PHP)的Haskell模拟$mybike->$position = 26?
没有任何外部模块,是否可以以优雅的方式进行?
否则,是否可以使用镜头?
是的,镜头正是您所需要的.
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
Run Code Online (Sandbox Code Playgroud)
要像你想要的那样使用:
repairedbike = replaceWheel mybike frontwheel 26
Run Code Online (Sandbox Code Playgroud)
你可以稍微削弱签名:
replaceWheel :: Bike -> Setter' Bike Wheel -> Wheel -> Bike
Run Code Online (Sandbox Code Playgroud)
这基本上只是一种奇特的说法
replaceWheel :: Bike
-> ((Wheel->Identity Wheel) -> (Bike->Identity Bike))
-> Wheel
-> Bike
Run Code Online (Sandbox Code Playgroud)
因为Identity它只是一个类型级别的同构,所以你最好省略它,最终会让你失望
replaceWheel :: Bike -> ((Wheel->Wheel) -> Bike->Bike) -> Wheel -> Bike
replaceWheel bike position wheel = bike & position (const wheel)
-- = position (const wheel) bike
Run Code Online (Sandbox Code Playgroud)
这可以这样称呼:
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
Run Code Online (Sandbox Code Playgroud)
所以,你确实没有严格要求任何图书馆!
使用合适的镜头而不是这种临时近似的原因包括:
Lens'可以用于设置,获取(和遍历)值.这只能在没有使用的基础Rank2多态性的情况下表达得很尴尬lens.(Wheel -> Wheel) -> Bike -> Bike可以做各种垃圾; lens需要镜头规律,基本上保证镜头实际上像记录存取器一样工作,仅此而已.顺便说一句,对于"修改某些东西"的函数,它在Haskell中是常规的,以便最后修改参数:
replaceWheel :: Setter' Bike Wheel -> Wheel -> Bike -> Bike
replaceWheel position wheel = position .~ wheel
Run Code Online (Sandbox Code Playgroud)
......或者更短,
replaceWheel = (.~)
Run Code Online (Sandbox Code Playgroud)