我有一些与普通树非常相似的数据类型,只是一些特殊的形式。
data NestedTree = NT
{ _dummy :: Int
, _tree :: HashMap String NestedTree
} deriving (Show)
makeLenses ''NestedTree
我想使用镜头强制初始化我的数据类型的实例。这是我现在得到的:
example :: NestedTree
example = flip execState (NT 0 mempty) $ do
dummy .= 3
tree.at "foo" ?= flip execState (NT 0 mempty) (dummy .= 10)
您可以在此示例中观察我可以使用(NT 0 mempty)
替换第一个(NT 3 mempty)
,但这不是重点。我想要的是也能够使用这种不错的命令式样式来初始化嵌套的HashMap
。更确切地说,我希望能够写出这样的东西:
example :: NestedTree
example = flip execState (NT 0 mempty) $ do
dummy .= 3
tree.at "foo" ?= flip execState (NT 0 mempty) $ do
dummy .= 10
tree.at "foo nested" ?= NT 5 mempty
tree.at "bar" ?= flip execState (NT 0 mempty) $ do
dummy .= 15
tree.at "bar nested" ?= NT (-3) mempty
我的真实数据结构更加复杂,很快就会使用简单的记录来初始化它。因此我想使用某种DSL,镜头非常适合我的需要。但是你可以注意到上面的代码没有编译。
这是因为($)
的优先级最低,我不能只写tree.at "foo" ?= flip execState (NT 0 mempty) $ do
。但我真的不想在嵌套()
周围添加do
。
有没有很好的方法可以将任意运算符与$
和do
混合以编写这样的函数?我真的不想介绍像wordsAssign = (?=)
这样的帮手,并调用像
wordsAssign (tree.at "foo") $ flip execState (NT 0 mempty) $ do
因为我喜欢?=
运算符。也许我做错了什么,我想做的这种事情可以在没有镜头的情况下用一些手写的操作员来完成?
答案 0 :(得分:3)
zoom
是为处理嵌套状态更新而量身定制的。在你的情况下,不幸的是,Maybe
- 使得使用它有点尴尬:
example :: NestedTree
example = flip execState (NT 0 mempty) $ do
dummy .= 3
zoom (tree.at "foo") $ do
put (Just (NT 0 mempty))
_Just.dummy .= 10
_Just.tree.at "foo nested" ?= NT 5 mempty
-- Or, using zoom one more time:
zoom (tree.at "bar") $ do
put (Just (NT 0 mempty))
zoom _Just $ do
dummy .= 15
tree.at "bar nested" ?= NT (-3) mempty
为了便于比较,如果您不需要在外层插入新密钥,则可以使用ix
代替at
并删除所有Maybe
相关的样板:
zoom (tree.ix "foo") $ do
dummy .= 10
tree.at "foo nested" ?= NT 5 mempty
答案 1 :(得分:2)
您可以使用与?=
相同的优先级定义自己的$
,以使它们在一起玩得很好:
import Control.Lens hiding ((?=))
import qualified Control.Lens as L
(?=)
:: MonadState s m
=> ASetter s s a (Maybe b) -> b -> m ()
(?=) = (L.?=)
infixr 0 ?=
这样你的例子就可以了。