Haskell:根据变量

时间:2016-09-17 20:41:13

标签: 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,而是被解释为position的(不存在的)字段Bike

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

是否可以以优雅的方式没有任何外部模块?

否则,是否可以使用镜头?

1 个答案:

答案 0 :(得分:6)

是的,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多态性,这只能表达得很尴尬。
  • 更简洁。上述类型有很多冗余;镜头为您提供这些访问者的简短同义词。
  • 更安全。函数(Wheel -> Wheel) -> Bike -> Bike可以做各种垃圾; lens需要镜头定律,基本上保证镜头实际上像记录存取器一样工作,仅此而已。
  • 快速。镜头库中的组合器在编写时考虑了性能(即支持流融合的内联,省略了在monad等中的复制)。

BTW,对于“修改某些东西”的函数,它在Haskell中是常规的,以便最后修改参数:

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

......或者更短,

replaceWheel = (.~)