提问人:pe200012 提问时间:11/3/2023 更新时间:11/3/2023 访问量:61
提升状态单子以专注于记录的一部分
Lift state monad to focus on part of a record
问:
使用以下代码片段:
data Circle = Circle
{ center :: Point
, radius :: Double
}
data Point = Point (Double, Double)
someFuncOverPoint :: State Point blahblah
我想知道是否有功能可以使 to focus on :someFuncOverPoint
Circle
someMagicFunc :: ??? -> State Point blahblah -> State Circle blahblah
也许这可以使用镜头来实现?
答:
1赞
willeM_ Van Onsem
11/3/2023
#1
严格来说,你可以。事实上,我们可以通过首先生成第一个 ,然后将其传递给 ,所以:State Point a
State
someMagicFunc :: Double -> State Point a -> State Circle a
someMagicFunc r s = State (\s0 -> let ~(s1, a) = runState s s0 in (Circle s1 r, a))
因此,在这里我们构造一个函数,该函数将初始状态映射到下一个状态和结果,然后我们将其转换为具有 as 状态和“结果”的 2 元组。State
s0
s1
Circle
a
话虽如此,更改状态的类型有点奇怪。通常,状态的类型在所有操作中保持不变。
3赞
pe200012
11/3/2023
#2
感谢@willeM_VanOnsem的回答。
这里的动机是,我有一些分层的数据结构和一些有效的函数,它们只关注部分上下文,所以我需要一种方法来组合它们。
我认为有更好的方法:
someMagicFunc :: State Point a -> State Circle a
someMagicFunc s = state (\s0 -> let ~(a, s1) = runState s (center s0)
in (a, s0 { center = s1 }))
我们可以反转箭头:
revMagic :: Double -> State Circle a -> State Point a
revMagic r s = state (\s0 -> let ~(a, s1) = runState s (Circle s0 r)
in (a, center s1))
更新:经过一番研究,我发现我需要的实际上是 缩放类型 .zoom :: Monad m => Lens' s a -> StateT a m r -> StateT s m r
一般来说,如果我们想在状态单子中向下转换上下文,这是微不足道的,但如果我们想向上转换,我们可能需要像棱镜这样的东西来构造数据。
上一个:菜单中的 Tkinter 菜单
下一个:查找突变字段值
评论
fmap :: (a -> b) -> StateT s m a -> StateT s m b
Circle