使用镜头访问仿函数内部的数据
Using lenses for accessing data inside functors
我发现 lenses 对于访问深度嵌套的数据非常有用,但经常 "containers" 像 MVar
或 TVar
s 抛弃了 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 不需要被解除,合成仍然是用普通的 (.)
.
我发现 lenses 对于访问深度嵌套的数据非常有用,但经常 "containers" 像 MVar
或 TVar
s 抛弃了 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 不需要被解除,合成仍然是用普通的 (.)
.