您如何使用镜头库来编写依赖于其他镜头的复杂镜头? [英] How do you write a complex lens that depend on other lenses using the lens library?

查看:134
本文介绍了您如何使用镜头库来编写依赖于其他镜头的复杂镜头?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前,我有一个 WorkLog 类型,其中包含开始日期和结束日期。我还想添加一个持续时间镜头,它将从开始和结束日期中派生出来。它应该是只读的,或者改变结束日期,如果它的值被改变了(我想知道如何实现这两个版本,即使我只使用一个)。



这是我的代码。基本上,如果您可以实现 workLogDurationRO workLogDurationRW 函数来获得主要传递中的所有测试,那将回答我的

  { - #LANGUAGE TemplateHaskell# - } 
模块Main其中
import Control.Lens

- 保持这个例子的简单时间
newtype TimeStamp = TimeStamp Int派生(Show,Eq)
newtype TimeDifference = TimeDifference Int派生(Show,Eq)

(-.-):: TimeStamp - > TimeStamp - > TimeDifference
(TimeStamp a)-.-(TimeStamp b)= TimeDifference(a - b)

data WorkLog = WorkLog {
_workLogDescription :: String
,_workLogStartTime :: TimeStamp
,_workLogEndTime :: TimeStamp
}

makeLenses''WorkLog

- |只需返回开始和结束时间之间的差异
workLogDurationRO :: Getter WorkLog TimeDifference
workLogDurationRO =错误TODO写信给我!

- |像只读版本一样,但与setter一起使用时,
- 更改结束日期。
workLogDurationRW :: Lens'WorkLog TimeDifference
workLogDurationRW =错误TODO写给我!

确保:: String - >布尔 - > IO()
确保_ True = putStrLn测试通过
确保msg False = putStrLn $测试失败:++ msg
$ b $ main :: IO()
main = do
let testWorkLog = WorkLogWork 1(TimeStamp 40)(TimeStamp 100)
确保只读透镜得到正确的持续时间$
testWorkLog ^ .workLogDurationRO == TimeDifference 60
确保读取+写入镜头获得正确的持续时间$
testWorkLog ^ .workLogDurationRW == TimeDifference 60
let newWorkLog = testWorkLog& workLogDurationRW。〜TimeDifference 5
确保可写镜头更改结束时间$
newWorkLog ^ .workLogEndTime == TimeStamp 45


解决方案

您可以使用来将 Getter 写入(你可以给 -.- 更低的优先级去掉括号):

  workLogDurationRO = to $ \wl  - > (wl ^ .workLogEndTime)-.-(wl ^ .workLogStartTime)

但是,镜头维基说,你可能最好用计算时间差的正常函数,你可以使用当你需要它作为镜头时。



你可以建立 Lens'来自getter(与上面相同)和setter:

  workLogDurationRW =镜头get set 
where
get :: WorkLog - > TimeDifference
get wl =(wl ^ .workLogEndTime)-.-(wl ^ .workLogStartTime)

set :: WorkLog - > TimeDifference - > WorkLog
set wl timeDiff = wl& workLogEndTime。〜(wl ^ .workLogStartTime)+。+ timeDiff
其中
TimeStamp a +。+ TimeDifference b = TimeStamp(a + b)


At the moment, I have a WorkLog type, with a start and end date. I want to also add a duration lens, which will be derived from the start and end dates. It should either be read only, or change the end date if its value is changed (I would like to know how to implement both version, even though I will only use one).

Here is my code. Basically, if you can implement the workLogDurationRO and workLogDurationRW functions to get all the tests in main passing, that would answer my question.

{-# LANGUAGE TemplateHaskell #-}
module Main where
import Control.Lens

-- Keep times simple for this example
newtype TimeStamp = TimeStamp Int deriving (Show, Eq)
newtype TimeDifference = TimeDifference Int deriving (Show, Eq)

(-.-) :: TimeStamp -> TimeStamp -> TimeDifference
(TimeStamp a) -.- (TimeStamp b) = TimeDifference (a - b)

data WorkLog = WorkLog {
  _workLogDescription :: String
  , _workLogStartTime :: TimeStamp
  , _workLogEndTime :: TimeStamp
  }

makeLenses ''WorkLog

-- | Just return the difference between the start and end time
workLogDurationRO :: Getter WorkLog TimeDifference
workLogDurationRO = error "TODO write me!"

-- | Like the read only version, but when used with a setter,
-- change the end date.
workLogDurationRW :: Lens' WorkLog TimeDifference
workLogDurationRW = error "TODO write me!"

ensure :: String -> Bool -> IO ()
ensure _ True = putStrLn "Test Passed"
ensure msg False = putStrLn $ "Test Failed: " ++ msg

main :: IO ()
main = do
  let testWorkLog = WorkLog "Work 1" (TimeStamp 40) (TimeStamp 100)
  ensure "read only lens gets correct duration" $ 
     testWorkLog^.workLogDurationRO == TimeDifference 60
  ensure "read+write lens gets correct duration" $ 
     testWorkLog^.workLogDurationRW == TimeDifference 60
  let newWorkLog = testWorkLog & workLogDurationRW .~ TimeDifference 5
  ensure "writeable lens changes end time" $ 
     newWorkLog^.workLogEndTime == TimeStamp 45

解决方案

You can write the Getter using to (you could give -.- lower precedence to get rid of the parentheses):

workLogDurationRO = to $ \wl -> (wl^.workLogEndTime) -.- (wl^.workLogStartTime)

But as the lens wiki says, you're probably better off with a normal function that computes the time difference, which you can then use with to when you need it as a lens.

You can build the Lens' from a getter (same as above) and a setter:

workLogDurationRW = lens get set
  where
    get :: WorkLog -> TimeDifference
    get wl = (wl^.workLogEndTime) -.- (wl^.workLogStartTime)

    set :: WorkLog -> TimeDifference -> WorkLog
    set wl timeDiff = wl & workLogEndTime .~ (wl^.workLogStartTime) +.+ timeDiff
      where
        TimeStamp a +.+ TimeDifference b = TimeStamp (a + b)

这篇关于您如何使用镜头库来编写依赖于其他镜头的复杂镜头?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆