使用镜片访问函子内部数据

4

我认为镜头在访问深层嵌套数据时非常有用,但是一些"容器"(如MVarTVar)经常破坏了镜头的某些好的性质。

例如:

data SomeStruct = SomeStruct { _b :: Int
                             }
makeLenses ''SomeStruct

data AppState = AppState { _a :: SomeStruct
                         }
makeLenses ''AppState

data App = App { _state :: AppState
               }
makeLenses ''App

我可以使用非常好的从左到右组合来制作新的镜头:

let v = App (AppState (SomeStruct 3))
in v^.state.a.b

然而,如果_state的类型是TVar,那么从左到右的组合就会崩溃,使用镜头会感觉非常笨重:
t <- newTVarIO $ AppState (SomeStruct 3)
let v = App t
atomically $ (^.a.b) <$> readTVar (v^.state)

^.a.b被推到左侧,尽管^.state是最内层的lens。有没有更加人性化的方式来处理这些“容器”类型和lenses?


2
有趣的想法!我做了一些谷歌搜索,发现Ed Kmett本人在Reddit上的这篇文章,他说你不能在不违反镜头定律的情况下写入可变位置。 - Benjamin Hodgson
1个回答

3

我是一名有用的助手,可以为您翻译文本。

有一个库(曾经是 lens 的一部分)叫做 lens-action,它帮助将获取器和折叠与单子操作混合在一起,而不会让你太过脱离 lensy 世界。

例如,对于以下类型:

data App = App { _state :: TVar AppState }

我们可以写
ghci> :t \v -> v^!state.act readTVar.a.b
\v -> v^!state.act readTVar.a.b :: App -> STM Int

这个想法是,我们不使用典型的视图函数(^.),而是使用它的单子版本(^!)。我们可以使用像actacts这样的函数插入单子操作。普通的getter和fold不需要提升,组合仍然使用纯粹的(.)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接