我正在阅读Dynamic programming example,有这样的代码:
buy n = r!n
where r = listArray (0,n) (Just (0,0,0) : map f [1..n])
f i = do (x,y,z) <- attempt (i-6)
return (x+1,y,z)
`mplus`
do (x,y,z) <- attempt (i-9)
return (x,y+1,z)
`mplus`
do (x,y,z) <- attempt (i-20)
return (x,y,z+1)
attempt x = guard (x>=0) >> r!x
我的问题是attempt x = guard (x>=0) >> r!x
是如何运作的?
根据此Control.Monad源代码,
guard True = pure ()
guard False = empty
pure :: a -> f a
m >> k = m >>= \_ -> k
所以如果x> 0,那么:
attempt x
= (guard True) >> (r!x) = (pure ()) >> (r!x)
= (pure ()) >>= \_ -> r!x = (f ()) >>= (\_ -> r!x)
因此f ()
应该是m a
类型(在这种情况下为Maybe a
),但Haskell如何知道f
是什么? f ()
可能会返回empty
,因为它从未被指定过。 (f
表示纯粹的f
如果x <0,empty
不在Maybe
中,那么这仍然适用于>>=
?
答案 0 :(得分:3)
这是一个多个问题,但让我们看看我是否可以让事情变得更清楚。
Haskell在解释f
时如何知道pure ()
是什么? pure
是一个类型类方法,所以这只是来自我们所处类型的实例声明。这最近改变了,所以你可能必须按照不同的路径来达到答案,但结果最终会相同:pure
for Maybe
is defined as Just
。
以同样的方式,empty
is in Maybe
, and is defined as Nothing
。
通过在ghci提示符下键入:i pure
或:i empty
,您将找到类型类提供这些功能的内容。那么你可以寻找实例声明Maybe
为他们做的。
从SO的角度来看,这很不幸,最近发生了变化,所以在不知道你正在使用的特定版本的情况下,没有明确的永久答案。希望这很快就会解决。
答案 1 :(得分:2)
在您对attempt x
的手动评估的最后一个表达中,您正在混淆类型和值。 pure :: a -> f a
不是定义;它是一种类型签名(请注意::
)。要完全引用它,pure
的类型是:
GHCi> :t pure
pure :: Applicative f => a -> f a
此处,f
代表Applicative
的任何实例,a
代表任何类型。在您的情况下,您正在使用Maybe
monad / applicative仿函数,因此f
为Maybe
。 pure ()
的类型为Maybe ()
。 (() :: ()
是您对结果不感兴趣时使用的虚拟值。()
中的pure ()
是值,但()
中的Maybe ()
是一个类型 - ()
值的类型。
我们将从您评估的最后一个正确步骤继续:
(pure ()) >>= \_ -> r!x
Haskell如何知道[
pure ()
]是什么?
从某种意义上说,它并不需要。这里使用pure ()
的函数是(>>=)
。它有以下类型:
GHCi> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
将m
设置为Maybe
,就像您的情况一样,我们得到:
Maybe a -> (a -> Maybe b) -> Maybe b
第一个参数的类型是Maybe a
,因此(>>=)
能够处理任何Maybe a
值,包括pure ()
,无论它是否为{{} 1}} - 某事或Just
。当然,它会处理Nothing
和Just
不同,因为这是the Monad
instance的全部要点:
Nothing
我们仍然需要完成评估。为此,我们需要了解(Just x) >>= k = k x
Nothing >>= _ = Nothing
的{{1}}定义方式。我们可以在the Applicative
instance of Maybe
中找到定义:
pure
现在我们终于可以继续了:
Maybe