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

如何实现单子式的Map.adjust函数?Haskell技术问询

解决方案

背景

现有可运行的Haskell除法函数:

divide :: Int -> Int -> Either String Int
divide a 0 = Left "Error with 0"
divide a b = Right (a `div` b)

我们需要改造调用逻辑,让调整Map值的操作支持错误反馈:

-- 原调用(仅纯函数逻辑,无法返回错误)
Map.adjust (divide 3) "A" $ Map.fromList [("A",3),("B",0)]

期望实现以下效果:

  • 当调整合法键(如"A")时,返回Right包裹的更新后Map:
    adjustM (divide 3) "A" $ Map.fromList [("A",3),("B",0)] 
    =>  Right $ Map.fromList [("A",1),("B",0)]   
    
  • 当调整触发错误的键(如"B")时,返回Left错误信息:
    adjustM (divide 3) "B" $ Map.fromList [("A",3),("B",0)]
    =>  Left "Error with 0"
    

本质是要实现一个支持monadic操作的通用调整函数,类型签名如下:

adjustM :: (Monad m) => (a -> m a) -> k -> Map k a -> m (Map k a)

实现代码

标准库的Data.Map.adjust是纯函数,不支持monadic上下文,我们可以自己实现这个增强版本:

import qualified Data.Map as Map

adjustM :: (Monad m) => (a -> m a) -> Map.Key -> Map.Map Map.Key a -> m (Map.Map Map.Key a)
adjustM f key map =
  case Map.lookup key map of
    -- 键不存在时,直接返回原Map的monadic包装
    Nothing -> return map
    -- 键存在时,先执行monadic操作更新值,再插入原Map
    Just val -> do
      newVal <- f val
      return $ Map.insert key newVal map

测试验证

用divide函数测试两种场景:

  1. 合法调整场景:
    adjustM (divide 3) "A" $ Map.fromList [("A",3),("B",0)]
    -- 输出:Right (fromList [("A",1),("B",0)])
    
  2. 错误触发场景:
    adjustM (divide 3) "B" $ Map.fromList [("A",3),("B",0)]
    -- 输出:Left "Error with 0"
    

说明

adjustM是通用函数,支持任意Monad实例(不限于Either String)。核心逻辑是:先查找目标键,存在则执行传入的monadic函数更新值,不存在则保持原Map不变,整个过程都包裹在Monad上下文中,自然传递成功/失败状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 15:55:58