haskell 提升状态monad以聚焦于记录的一部分

wsxa1bj1  于 2023-11-18  发布在  其他
关注(0)|答案(2)|浏览(112)

下面的代码片段:

data Circle = Circle 
            { center :: Point
            , radius :: Double
            }

data Point = Point (Double, Double)

someFuncOverPoint :: State Point blahblah

字符串
我想知道是否有一个函数可以使someFuncOverPoint专注于Circle

someMagicFunc :: ??? -> State Point blahblah -> State Circle blahblah


也许这可以用透镜实现?

uujelgoq

uujelgoq1#

感谢@willeM_VanOnsem的answer
这里的动机是,我有一些层次化的数据结构和一些只关注部分上下文的有效函数,所以我需要一种方法来组合它们。
我想有个更好的办法:

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类型的缩放。
一般来说,如果我们想向下转换state monad中的上下文,这是微不足道的,但是如果我们想向上转换,我们可能需要像prism这样的东西来构造数据。

nhn9ugyo

nhn9ugyo2#

严格地说,你可以**。事实上,我们可以通过首先生成第一个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))

字符串
所以这里我们构造一个State,它与一个函数一起工作,该函数将初始状态s0Map到下一个状态s1和结果,然后我们将其转换为一个二元组,其中Circle作为状态,“结果”a也是。
也就是说,改变状态的类型有点奇怪。通常状态的类型在所有动作上都保持不变。

相关问题