提升状态单子以专注于记录的一部分

Lift state monad to focus on part of a record

提问人:pe200012 提问时间:11/3/2023 更新时间:11/3/2023 访问量:61

问:

使用以下代码片段:

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

data Point = Point (Double, Double)

someFuncOverPoint :: State Point blahblah

我想知道是否有功能可以使 to focus on :someFuncOverPointCircle

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

也许这可以使用镜头来实现?

函数编程 Haskell-Lens

评论

0赞 pe200012 11/3/2023
@cafce25 不会更改 s 的类型fmap :: (a -> b) -> StateT s m a -> StateT s m b
0赞 willeM_ Van Onsem 11/3/2023
这很奇怪,所以你想改变状态变量的类型吗?
0赞 willeM_ Van Onsem 11/3/2023
另一个问题:我们正在建造的半径是多少?Circle
0赞 pe200012 11/3/2023
@willeM_VanOnsem我以为我们可以为它提供默认值(可能是 0)......?

答:

1赞 willeM_ Van Onsem 11/3/2023 #1

严格来说,你可以。事实上,我们可以通过首先生成第一个 ,然后将其传递给 ,所以:State Point aState

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 元组。States0s1Circlea

话虽如此,更改状态的类型有点奇怪。通常,状态的类型在所有操作中保持不变。

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

一般来说,如果我们想在状态单子中向下转换上下文,这是微不足道的,但如果我们想向上转换,我们可能需要像棱镜这样的东西来构造数据。