如何向Control.Monad.Reader实例传递额外参数
你尝试写出的类型签名是完全合法的Reader monad扩展用法,核心逻辑利用了Reader的本质:Reader r a等价于r -> a的函数封装,当a本身为函数类型时,就可以实现「先读取环境、再接收其余参数返回结果」的逻辑。
两种等价实现方案
方案1:返回函数的Reader(你尝试的写法)
这种写法直接把需要额外接收的参数作为返回值的函数参数:
-- 原非Reader实现 getArguments :: Config -> Request -> Arguments getArguments cfg req = take (cfgMaxArgs cfg) (reqParams req) -- 转为Reader版本,等价于封装了 Config -> (Request -> Arguments) getArguments :: Reader Config (Request -> Arguments) getArguments = asks $ \cfg req -> take (cfgMaxArgs cfg) (reqParams req) -- produceResponse同理 produceResponse :: Reader Config (Header -> Arguments -> Response) produceResponse = asks $ \cfg header args -> Response { respStatus = 200, respHeaders = cfgDefaultHeaders cfg <> [header], respBody = encode args }
对应的handle函数实现:
handle :: Request -> Reader Config Response handle req = do header <- initializeHeader -- 无额外参数的Reader,直接获取Header getArgsFn <- getArguments -- 取出 Request -> Arguments 函数 let args = getArgsFn req -- 传入req得到参数 produceRespFn <- produceResponse -- 取出多参数函数 pure $ produceRespFn header args -- 传入所有非环境参数得到结果
方案2:参数前置返回Reader(更常用的写法)
这种写法可读性更高,把非环境参数放在参数列表最前面,直接返回Reader,等价于方案1的柯里化转换:
-- 额外参数提前传入,返回只需读取环境就能得到结果的Reader getArguments :: Request -> Reader Config Arguments getArguments req = asks $ \cfg -> take (cfgMaxArgs cfg) (reqParams req) produceResponse :: Header -> Arguments -> Reader Config Response produceResponse header args = asks $ \cfg -> Response { respStatus = 200, respHeaders = cfgDefaultHeaders cfg <> [header], respBody = encode args }
对应的handle函数和你看到的示例写法完全一致,更简洁:
handle :: Request -> Reader Config Response handle req = do header <- initializeHeader args <- getArguments req -- 直接传req,自动读取环境返回Arguments produceResponse header args -- 直接传参数,自动读取环境返回Response
方案选择建议
- 如果你需要拿到函数后多次传入不同参数调用,选择方案1,避免重复读取环境
- 日常开发中优先选择方案2,逻辑更直观,do块写法更简洁
内容的提问来源于stack exchange,提问作者senjin.hajrulahovic
相关产品推荐
相关产品推荐

