【问题标题】:How to use IORef with lenses?如何将 IORef 与镜头一起使用?
【发布时间】:2014-08-21 10:18:07
【问题描述】:
想知道如何最好地将Control.Lens 包与IORefs 结合起来。具体来说,我希望能够将atomicModifyIORef 与镜头一起使用,以便我可以提供a -> (a, b) 类型的函数并从操作中返回一个值。代码sn-p:
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
【问题讨论】:
标签:
haskell
atomic
haskell-lens
ioref
【解决方案1】:
原则上,需要的镜头操作员实际上是%%~,这只是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 的类型,除了你真的想要@987654333 @ 成为元组的最后一个元素而不是第一个元素。解决这个问题后,我得到了以下结果:
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
【解决方案2】:
我发现定义一个辅助函数来使用Lenses 操作IORefs 很方便。正如 Ørjan Johansen 所提到的,atomicModifyIORef 使用与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