使用镜头访问仿函数内部的数据

Using lenses for accessing data inside functors

我发现 lenses 对于访问深度嵌套的数据非常有用,但经常 "containers" 像 MVarTVars 抛弃了 lenses 的一些很好的特性。

例如:

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 是最里面的镜头。有什么方法可以让我以更符合人体工程学的方式处理这些类型的 "container" 类型和镜头吗?

有一个库(以前是 lens proper) called lens-action 的一部分,它有助于将 getter 和折叠与 monading 动作混合在一起,而不会把你从 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

我们的想法是,我们不使用典型的视图函数 (^.),而是使用它的单子函数 (^!). And we insert monadic actions using functions like act or acts。正常的 getters 和 folds 不需要被解除,合成仍然是用普通的 (.).