5

想知道如何最好地将Control.Lens包与IORefs 结合起来。具体来说,我希望能够atomicModifyIORef与镜头一起使用,这样我就可以提供一个类型的函数a -> (a, b)并从操作中返回一个值。代码片段:

let inc x = (x+1, x)
ior <- newIORef ((1, 1) :: (Int, Int))
thisShouldBe1 <- ior & atomicModifyIORef ?? _1 inc -- this is the bit I'm stuck on
4

2 回答 2

6

原则上,实际需要的镜头操作员是%%~,这只是方便的同义词id。但是,由于 和 中使用的元组顺序存在令人讨厌的不兼容atomicModifyIORef(,) a Functor它需要进行一些交换才能工作。我不认为生成的运算符是预定义的,但我在swappedId下面给出了初步名称。

请注意,Lens类型定义为

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t

事实证明,如果你让f成为(,) a Functor,这几乎完全符合你想要用来转换你的类型inc,除了你真的希望a成为元组的最后一个元素而不是第一个元素。解决这个问题后,我得到了以下结果:

import Data.IORef
import Control.Lens

l `swappedId` f = f & mapping swapped %~ l

main = do
    let inc x = (x+1, x)
    ior <- newIORef ((1, 1) :: (Int, Int))
    thisShouldBe1 <- atomicModifyIORef ior $ _1 `swappedId` inc
    print thisShouldBe1
    print =<< readIORef ior
于 2014-08-21T13:17:02.043 回答
3

我发现定义一个辅助函数来使用es操作IORefs很方便。Lens正如 Ørjan Johansen 所说,使用与 的实例atomicModifyIORef不同的配对顺序,因此我们需要交换。既然你想拥有 type 的功能,我们也需要交换这个功能。Functor(,)a -> (a, b)

{-# LANGUAGE RankNTypes #-}
import Control.Lens
import Data.IORef
import Data.Tuple (swap)

-- | Atomically modifies an 'IORef' using a lens
atomicModifyWithLens :: IORef a -> Lens a a b c -> (b -> (c, r)) -> IO r
atomicModifyWithLens ref l f =
    atomicModifyIORef ref (swap . traverseOf l (swap . f))

main = do
    let inc x = (x+1, x)
    ior <- newIORef ((1, 1) :: (Int, Int))
    thisShouldBe1 <- atomicModifyWithLens ior _1 inc
    print thisShouldBe1
于 2014-08-21T18:33:57.667 回答