给定以下结构:
这样做虽然有效,但是它假定我要绑定的函数始终返回具有相同纯度的计算结果,无论其输入是什么。在我看来,这种假设似乎合理,因为计算的纯度应该明确说明,而不依赖于某些计算,直到我注意到以下形式的函数与此假设不兼容:
所以在我的看来,这似乎是不可能的,因为我需要当前状态来应用第一个计算,以获取函数的正确输入,从而获得第二个计算并测试其是否纯净。
有人看到解决方案吗?还是有证据表明这是不可能的?
data Computation a = Pure (CState -> (a, CState))
| Action (CState -> IO (a, CState))
(CState是一个用于保持状态的结构,但现在并不太重要。)
现在我想把它变成Monad的一个实例,基本上只是一个状态monad,并且可以很容易地使用StateT实现。唯一的添加是,我想跟踪结果计算是纯的还是动作的,并且我想能够检查计算是否包含任何Action
,在执行Action
之前(这样Action
中的IO就不会被执行)。
还应该注意,Computation
有两个构造函数并不真的重要。我只是用这些构造函数开始实现这个。
确定a >> b
是否为Pure的规则很简单:
如果a
和b
都是Pure
,那么a >> b
是Pure
,否则它是一个动作。
现在我开始实现Monad实例:
instance Monad Computation where
return x = Pure $ \s -> (x, s)
(Action c) >>= f = Action . runStateT $
(StateT $ unpackAction oldComp) >>= (StateT . unpackAction . f)
p@(Pure c) >>= f
| givesPure f = Pure . runState $
state oldF >>= (state . unpackPure . f)
| otherwise = liftComp p >>= f -- Just lift the first argument and recurse, to make an action
-- Helper functions used above:
unpackAction :: Computation a -> (CState -> IO (a, CState))
unpackAction (Pure c) = return . c
unpackAction (Action c) = c
-- Make an Action out of a Pure
liftComp :: Computation a -> Computation a
liftComp (Pure c) = Action $ return . c
liftComp a@(Action _) = a
因此,唯一缺失的部分是 givesPure
函数,我不确定是否可能实现它。我曾经有过这样的一个实现:
givesPure :: (a -> Computation b) -> Bool
givesPure f = isPure $ f undefined -- I actually used error with a custom message instead of undefined, but that shouldn't matter
isPure :: Computation a -> Bool
isPure (Pure _) = True
isPure (Action _) = False
这样做虽然有效,但是它假定我要绑定的函数始终返回具有相同纯度的计算结果,无论其输入是什么。在我看来,这种假设似乎合理,因为计算的纯度应该明确说明,而不依赖于某些计算,直到我注意到以下形式的函数与此假设不兼容:
baz :: Int -> Computation b
baz x = if x > 5
then foo
else bar
-- foo and bar both have the type Computation b
所以在我的看来,这似乎是不可能的,因为我需要当前状态来应用第一个计算,以获取函数的正确输入,从而获得第二个计算并测试其是否纯净。
有人看到解决方案吗?还是有证据表明这是不可能的?