haskell - 如何使用镜头库编写依赖于其他镜头的复杂镜头?

标签 haskell haskell-lens

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

这是我的代码。基本上,如果您可以实现 workLogDurationROworkLogDurationRW 函数来获取主要传递中的所有测试,那就可以回答我的问题。

{-# 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

最佳答案

您可以使用 to 编写 Getter (您可以给予 -.- 较低的优先级以去掉括号):

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

但是作为 lens wiki说,你可能最好使用一个计算时间差的普通函数,然后当你需要它作为镜头时,你可以将它与 to 一起使用。

您可以从 getter(与上面相同)和 setter 构建 Lens':

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)

关于haskell - 如何使用镜头库编写依赖于其他镜头的复杂镜头?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21255541/

相关文章:

Haskell 从字符串或文本创建 UUID

windows - haskell ,GHC,win32,开罗

python - 如何在 Haskell 中重新实现这个 Python XML 解析函数?

haskell - 如何获得具有重载字段名称的经典镜头?

haskell - 设计代码以避免维护期间重写

haskell - OAuth2 证书应该如何存储在 Haskell 中

haskell - 单子(monad)内部的多态性

haskell - 是否可以使 Traversal 成为 IsString 的实例

haskell - 如何同时将透镜(或任何其他光学器件)视为 setter/getter 和设置剂?

haskell - 如何使用 Lenses 来表示 `mapM` 和 `concat` 以连接 IO 操作的结果?