讓我們通過這個!
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = _
那麼我們想要keepValue
做什麼?那麼,我們應該做的第一件事是使用ma
,所以我們可以將它連接到f
。
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
a <- ma
_
現在我們有a
類型的值va
,所以我們可以把它傳遞給f
。
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
va <- ma
vb <- f va
_
最後,我們要生產va
,這樣我們就可以做到這一點:
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
va <- ma
vb <- f va
return va
這是我如何完成編寫任何像這樣的一元函數的第一個草案行走。然後,我會清理它。首先,一些小事情:因爲Applicative
是Monad
的超類,所以我更喜歡pure
到return
;我們沒有使用vb
;我會放下v
這個名字。因此,對於這個功能的do
-notation基礎版本,我認爲最好的辦法是
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
a <- ma
_ <- f a
pure a
然而,現在,我們就可以開始讓實現更好的。首先,我們可以顯式調用替換_ <- f va
到(>>)
:
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
a <- ma
f a >> pure a
而現在,我們可以應用的簡化。你可能知道,我們總是可以fmap
/(<$>)
更換(>>=)
加pure
/return
:任何pure . f =<< ma
,ma >>= pure . f
,或者do a <- ma ; pure $ f a
(所有這些都是等價物)可以通過f <$> ma
更換。然而,Functor
型類有另一個,-公知的方法少,(<$)
:
(<$) :: a -> f b -> f a
替換輸入的所有位置具有相同值。默認定義是fmap . const
,但這可能會被更高效的版本覆蓋。
因此,我們必須對(<$)
類似的替換規則:我們可以隨時更換b <$ ma
或ma >> pure b
do ma ; pure b
。這給了我們
keepValue :: Monad m => m a -> (a -> m b) -> m a
keepValue ma f = do
a <- ma
a <$ f a
而且我認爲這是這個功能的最短合理版本!沒有任何不錯的免費技巧可以使這個更清潔。其中一個指標是在do
區塊的第二行上多次使用a
。
順便說一句,一個術語記:你在運行兩個單子行動,或兩個單子值;你沒有運行*「兩個monad」。單子就像Maybe
- 一種類型的構造函數,它支持(>>=)
和return
。不要將這些值與類型混合在一起 - 這種術語區分有助於保持更清晰的事物!
一般來說,你不能做任何事情來「扔掉」一個monadic動作的效果。如果你真的想保持兩種效果,那麼你的函數是'\ x f - > x >> = \ a - > f a >> return a'。 – user2407038
在最一般的情況下,單數函數給出的*結果*取決於它的* efect *(或其祕密),所以我不認爲你可以避免某些選擇的一元函數的效應。 (我正在考慮IO ......如果'ma'從標準輸入中取出'a'會怎麼樣?) – Euge