You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升State Monad以聚焦记录字段?能否用Lens实现转换?

使用Lens实现State类型的聚焦转换

完全可以用Lens实现你要的someMagicFunc,核心就是用Control.Lens里的zoom函数。

步骤说明:

  1. 定义Circle到Point的Lens:
    因为Circle包含center字段,我们需要一个能聚焦到该字段的Lens。可以手动定义,也用TemplateHaskell自动生成:

    • 手动定义Lens:

      import Control.Lens (Lens', zoom)
      import Control.Monad.State (State, get, put)
      
      data Point = Point (Double, Double) deriving (Show)
      data Circle = Circle 
                  { center :: Point
                  , radius :: Double
                  } deriving (Show)
      
      -- 手动定义聚焦到Circle的center字段的Lens
      centerLens :: Lens' Circle Point
      centerLens f (Circle c r) = fmap (\newCenter -> Circle newCenter r) (f c)
      
    • 用TemplateHaskell自动生成:
      如果导入Control.Lens.TH,可以通过命名约定自动生成Lens,更便捷:

      import Control.Lens (Lens', zoom, makeLenses)
      import Control.Monad.State (State, get, put)
      
      data Point = Point (Double, Double) deriving (Show)
      data Circle = Circle 
                  { _center :: Point  -- 字段名加下划线前缀
                  , _radius :: Double
                  } deriving (Show)
      
      makeLenses ''Circle  -- 自动生成center :: Lens' Circle Point
      
  2. 实现someMagicFunc:
    你要的someMagicFunc其实就是zoom结合上述Lens,它的类型完全符合你的需求:

    -- 用手动定义的centerLens
    someMagicFunc :: State Point a -> State Circle a
    someMagicFunc = zoom centerLens
    
    -- 如果用自动生成的Lens,直接写
    -- someMagicFunc = zoom center
    

原理说明

zoom函数的作用就是将作用于子结构的State操作,提升为作用于包含该子结构的父结构的State操作。在这里,它把操作Point的State Point blahblah,转换成操作Circle的State Circle blahblah,自动帮你处理父结构的拆包和重新打包——执行时会取出Circle的center字段,运行原State操作修改它,再把修改后的center放回原Circle中。

使用示例

假设我们有一个修改Point坐标的操作:

someFuncOverPoint :: State Point ()
someFuncOverPoint = do
  Point (x, y) <- get
  put $ Point (x + 1, y + 1)

用someMagicFunc转换后,就能直接作用于Circle:

-- 测试代码
import Control.Monad.State (execState)

testCircle :: Circle
testCircle = Circle (Point (0,0)) 5.0

main :: IO ()
main = print $ execState (someMagicFunc someFuncOverPoint) testCircle
-- 输出:Circle {_center = Point (1.0,1.0), _radius = 5.0}

内容的提问来源于stack exchange,提问作者pe200012

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:22:33