Haskell中如何提取reset/shift分隔续延为纯函数供后续使用?
提取分隔续延为独立纯函数的实现方法
问题背景
以下是基于reset/shift实现的分隔续延基础可运行示例:
import Control.Monad import Control.Monad.Trans import Control.Monad.Trans.Cont test :: Integer test = evalCont . reset $ do r <- shift $ \k -> do return $ k 10 return $ 1 + r
在GHCI中执行test将返回11,逻辑运行正常。
需求是不局限于在shift作用域内调用捕获的续延k,而是将k提取为可独立调用的纯函数。最初尝试的实现代码如下:
test2 :: Integer -> Integer test2 = evalCont . reset $ do r <- shift $ \k -> do return $ k return $ 1 + r
这段代码无法通过编译,GHC抛出如下类型不匹配错误:
? Couldn't match type 'Integer -> Integer' with 'Integer' Expected type: Cont (Integer -> Integer) (Integer -> Integer) Actual type: ContT (Integer -> Integer) Data.Functor.Identity.Identity ((Integer -> Integer) -> Integer -> Integer) ? In a stmt of a 'do' block: return $ k In the expression: do return $ k In the second argument of '($)', namely '\ k -> do return $ k' | 88 | return $ k | ^^^^^^^^^^
错误原因
类型不匹配的核心原因有两个:
shift捕获的续延k不是纯函数,类型为a -> Cont r b:接收输入后返回的是续延计算结构,而非直接输出纯值。- 直接返回
k时,k的返回类型与reset块要求的最终结果类型r形成递归类型约束,GHC无法自动完成类型对齐。
实现方案
方案1:手动包装shift捕获的续延
在shift块内拿到k后,手动将其转换为纯函数:对任意输入x,嵌套一层reset限定k x的执行边界,再通过evalCont运行计算拿到纯结果,包装后的函数即可脱离shift作用域独立调用。
test2 :: Integer -> Integer test2 = evalCont . reset $ do r <- shift $ \k -> do -- 将monadic续延转换为纯函数 return $ \x -> evalCont . reset $ k x return $ 1 + r
运行验证:
λ> test2 10 11 λ> test2 20 21
完全符合预期,提取出的续延等价于\r -> 1 + r。
方案2:使用control操作符替代shift
Control.Monad.Trans.Cont提供的control操作符对应Felleisen的F控制算子,它捕获的续延不会自动绑定reset边界,直接返回的k就是可独立使用的纯函数,代码更简洁:
test2' :: Integer -> Integer test2' = evalCont . reset $ do r <- control $ \k -> return k return $ 1 + r
运行效果和方案1完全一致。
内容的提问来源于stack exchange,提问作者chansey
相关产品推荐
相关产品推荐

