Haskell 理解“newtype Prob”的“bind”`
显示Haskell 理解“newtype Prob”的“bind”`,haskell,Haskell,显示Probnewtype: newtype Prob a=Prob{getProb::[(a,Rational)]}派生Show 以下是Prob的定义: instance Functor Prob where fmap f (Prob xs) = Prob $ map (\(x,p) -> (f x,p)) xs instance Monad Prob where return x = Prob [(x, 1%1)] p >>= f = f
Prob
newtype:
newtype Prob a=Prob{getProb::[(a,Rational)]}派生Show
以下是Prob
的定义:
instance Functor Prob where
fmap f (Prob xs) = Prob $ map (\(x,p) -> (f x,p)) xs
instance Monad Prob where
return x = Prob [(x, 1%1)]
p >>= f = flatten (fmap f p)
然后是支持功能:
flatten :: Prob (Prob a) -> Prob a
flatten = Prob . convert . getProb
convert :: [(Prob a, Rational)] -> [(a, Rational)]
convert = concat . (map f)
f :: (Prob a, Rational) -> [(a, Rational)]
f (p, r) = map (mult r) (getProb p)
mult :: Rational -> (a, Rational) -> (a, Rational)
mult r (x, y) = (x, r*y)
我编写了展平、转换、f和mult函数,所以我对它们很熟悉
然后,我们将>=
应用于以下示例,其中涉及一个数据类型,Coin
:
data Coin = Heads | Tails deriving (Show, Eq)
coin :: Prob Coin
coin = Prob [(Heads, 1%2), (Tails, 1%2)]
loadedCoin :: Prob Coin
loadedCoin = Prob [(Heads, 1%10), (Tails, 9%10)]
LYAH说,如果我们一次扔掉所有硬币,它们落在尾巴上的几率是多少?
flipTwo:: Prob Bool
flipTwo= do
a <- coin -- a has type `Coin`
b <- loadedCoin -- similarly
return (all (== Tails) [a,b])
flipTwo
可以使用>=
重新写入:
flipTwoBind' :: Prob Bool
flipTwoBind' = coin >>=
\x -> loadedCoin >>=
\y -> return (all (== Tails) [x,y])
我不理解返回的类型(全部(=尾部)[x,y])。因为它是>=
的右侧,所以它的类型必须是a->mb
(其中Monad m
)
我的理解是(all(=Tails)[x,y])
返回True或False
,但是返回如何导致上述结果:
Prob{getProb=[(False,1%20),(False,9%20),(False,1%20),(True,9%20)]
?请注意,=
运算符的RHS是一个lambda表达式,而不是return
的应用程序:
\y -> return (all (== Tails) [x,y])
此lambda具有预期的类型(Monad m)=>a->mb
让我们从底部构建类型:
正如您所说,all(=Tails)[x,y]
返回True
或False
。换句话说,它的类型是Bool
现在,在ghci中检查return
的类型,我们看到:
Prelude> :t return
return :: Monad m => a -> m a
所以return(all(=Tails)[x,y])
是类型Monad m=>m Boolean
将其包装在lambda中,然后给出类型(Monad m)=>a->m Boolean
(注意,在此过程中,编译器将推断出具体的monad类型是Prob
)
您应该将return
看作是取一个常规值并将其包装成一个Monad
添加:
让我们分析一下
flipTwoBind' = coin >>=
\x -> loadedCoin >>=
\y -> return (all (== Tails) [x,y])
我们首先注意到,这里最外层的表达式是(>>=)
的一个应用程序,其类型为:
Prelude> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
LHS是coin
,其类型为Prob coin
,因此我们立即推断m
是Prob
,a
是coin
。这意味着对于某些类型的b
,RHS必须具有类型Coin->Prob b
。现在让我们看看RHS:
\x -> loadedCoin >>= \y -> return (all (== Tails) [x,y])
这里我们有一个lambda,它返回(>>=)
应用程序的结果,因此lambda具有类型
(Monad m) => a -> m b
这与应用第一个(>>=)
的预期类型相匹配,因此a
这里是Coin
,m
是Prob
现在分析(>>=)
的内部应用程序,我们看到它的类型被推断为
(>>=) :: Prob Coin -> (Prob -> Prob b) -> Prob b
我们已经分析了第二个(>>=)
,因此b
被推断为Bool
(请注意,这可能不是编译器用来推断类型的确切顺序。它恰好是我分析此答案的类型时所遵循的顺序。)(我将称您的硬币fairCoin
)您有:
flipTwoBind' :: Prob Bool
flipTwoBind' = fairCoin >>= g where
g x = loadedCoin >>= h where
h y = return z where
z = all (== Tails) [x,y]
根据(>>=)
的类型,我们得到:
fairCoin :: Prob Coin
(>>=) :: Monad m => m a -> (a -> m b) -> m b | m ~ Prob, a ~ Coin
fairCoin >>= g :: m b | g :: Coin -> Prob b
flipTwoBind' :: Prob Bool | m ~ Prob, b ~ Bool
因此,g::Coin->Prob Bool
和gx::Prob Bool
提供了x::Coin
由于gx=loadedCoin>=h
,我们有
loadedCoin :: Prob Coin
(>>=) :: Monad m => m a -> (a -> m b) -> m b
loadedCoin >>= h :: Prob Bool
所以,h::Coin->Prob Bool
,z::Bool
和返回z::Prob Bool
:
all :: (a -> Bool) -> [a] -> Bool
all p [] :: Bool
return :: (Monad m) => a -> m a
z :: Bool
return z :: m Bool | m ~ Prob so return z :: Prob Bool
由于Prob a
本质上是一个标记的a
结果对及其相应概率的关联列表,Prob Bool
是一个Bool
结果对及其概率的列表
翻译为特定的Prob
一元代码,内联所有函数,flipTwoBind'
成为
flipTwoBind' = fairCoin >>= g
= flatten (fmap g fairCoin)
= Prob . convert . getProb $
Prob $ map (\(x,p) -> (g x,p)) $ getProb fairCoin
= Prob . concat . map (\(x,p) -> map (\(x, y) -> (x, p*y)) $ getProb x)
. map (\(x,p) -> (g x,p)) $ getProb fairCoin
(请参见内部的Prob
和getProb
相互抵消的效果…)
切换到基于普通列表的代码(使用gL xs=getProb(g(Prob xs))
和fairCoinL=getProb fairCoin
等),相当于
= concat . map (\(x,p) -> map (second (p*)) x)
. map (\(x,p) -> (gL x,p)) $ fairCoinL
= concat . map (\(x,p) -> map (second (p*)) $ gL x) $ fairCoinL
= [(v,p*q) | (x,p) <- fairCoinL, (v,q) <- gL x]
= ....
= [(z,r) | (x,p) <- [(Heads, 1%2), (Tails, 1%2 )], -- do a <- fairCoin
(y,q) <- [(Heads, p*1%10), (Tails, p*9%10)], -- b <- loadedCoin
(z,r) <- [(all (== Tails) [x,y], q*1%1 )] ] -- return ... all ...
= [(False,1 % 20),(False,9 % 20),(False,1 % 20),(True,9 % 20)]
=concat。地图(\(x,p)->地图(第二(p*)x)
. 地图(\(x,p)->(gL x,p))$fairCoinL
=concat。地图(\(x,p)->地图(第二(p*)$gL x)$fairCoinL
=[(v,p*q)|(x,p)谢谢,代码学徒。关于你能说得更多吗请注意,在这个过程中,编译器会推断出具体的monad类型是Prob
?我相信Prob
的定义,即flatte(fmap fp)>=
的正在发挥作用,但我不清楚。@KevinMeredith我不完全确定这些细节。我猜你必须分析flipTwoBind'
的完整定义的类型。最有可能的是从硬币和loadedCoin
的类型中进行推断第二,我增加了对类型推断的更详细分析
= concat . map (\(x,p) -> map (second (p*)) x)
. map (\(x,p) -> (gL x,p)) $ fairCoinL
= concat . map (\(x,p) -> map (second (p*)) $ gL x) $ fairCoinL
= [(v,p*q) | (x,p) <- fairCoinL, (v,q) <- gL x]
= ....
= [(z,r) | (x,p) <- [(Heads, 1%2), (Tails, 1%2 )], -- do a <- fairCoin
(y,q) <- [(Heads, p*1%10), (Tails, p*9%10)], -- b <- loadedCoin
(z,r) <- [(all (== Tails) [x,y], q*1%1 )] ] -- return ... all ...
= [(False,1 % 20),(False,9 % 20),(False,1 % 20),(True,9 % 20)]
= [(all (== Tails) [x,y], q) -- ... all ... <$>
| (x,p) <- [(Heads, 1%2), (Tails, 1%2 )], -- fairCoin <*>
(y,q) <- [(Heads, p*1%10), (Tails, p*9%10)] ] -- loadedCoin