[英]Standard Haskell function :: (a -> Maybe b) -> [a] -> Maybe b
[英]Is there a standard Haskell function for (return a) >>= b?
我正在尋找一種從以下函數中刪除return
的方法:
naming path =
getModificationTime path >>=
return . formatTime defaultTimeLocale "%Y%m%d" >>=
return . printf "%s_%s" (takeBaseName path) >>=
return . replaceBaseName path
我這樣構造它的原因是因為>>=
本質上變成了一種管道操作符,數據從一行流向下一行。
我想我可以沿着這條線定義一個算子
a |>= b = (return a) >>= b
得到
naming path =
getModificationTime path >>=
formatTime defaultTimeLocale "%Y%m%d" |>=
printf "%s_%s" (takeBaseName path) |>=
replaceBaseName path
但我得到了錯誤
Precedence parsing error
cannot mix `|>=' [infixl 9] and `.' [infixr 9] in the same infix expression
解決此問題的最佳方法是什么? 更好的是,是否有某種標准的運算符或其他方式可以更容易地以這種方式構造代碼?
據推測,您希望它具有與>>=
相同的固定性,因此如果您加載GHCi並鍵入
> :info (>>=)
...
infixl 1 >>=
然后,您可以將運營商定義為
infixl 1 |>=
(|>=) :: Monad m => a -> (a -> m b) -> m b
a |>= b = return a >>= b
但是,如果你的monad保留monad定律,這與只做ba
相同,所以首先不需要操作符。
我還建議使用do notation:
naming path = do
modTime <- getModificationTime path
let str = formatTime defaultTimeLocale "%Y%m%d" modTime
name = printf "%s_%s" (takeBaseName path) str
replaceBaseName path name
您需要return
因為鏈的整個其余部分組成了純函數,因此最好的方法是使用普通函數組合運算符.
。 但是, .
與>>=
相比,在不同的方向上編寫,因為>>=
用於組成兩個monadic操作,你需要至少一個返回:
naming path =
getModificationTime path >>=
return .
replaceBaseName path .
printf "%s_%s" (takeBaseName path) .
formatTime defaultTimeLocale "%Y%m%d"
擺脫額外return
一種方法是使用<**>
和pure
來自Control.Applicative
而不是>>=
。
naming path =
getModificationTime path <**> pure (
replaceBaseName path .
printf "%s_%s" (takeBaseName path) .
formatTime defaultTimeLocale "%Y%m%d" )
為了“修復”從上到下流動的操作順序,我們可以替換.
使用來自Control.Category
>>>
,給出
naming path =
getModificationTime path <**> pure (
formatTime defaultTimeLocale "%Y%m%d" >>>
replaceBaseName path >>>
printf "%s_%s" (takeBaseName path) )
或者,如果你想變得瘋狂(使用Control.Arrow
):
naming path = flip runKleisli path $
Kleisli getModificationTime >>^
formatTime defaultTimeLocale "%Y%m%d" >>^
replaceBaseName path >>^
printf "%s_%s" (takeBaseName path)
不幸的是, Control.Applicative
沒有提供<$>
的翻轉版本,但您可以自己定義它以獲得更整潔的版本:
(<$$>) = flip (<$>)
infixr 1 <$$>
naming path =
getModificationTime path <$$>
formatTime defaultTimeLocale "%Y%m%d" >>>
replaceBaseName path >>>
printf "%s_%s" (takeBaseName path)
在這一點上,我們可以這樣做:
(|>=) = flip fmap
naming path =
getModificationTime path |>=
formatTime defaultTimeLocale "%Y%m%d" |>=
printf "%s_%s" (takeBaseName path) |>=
replaceBaseName path
return x >>= f
與fx
相同---換句話說,你的大部分內部函數都是純函數,根本不需要(>>=)
。 在do
notation中你使用let
來綁定純計算。
naming path = do
time <- getModificationTime path
let str = formatTime defaultTimeLocale "%Y%m%d" time
name = printf "%s_%s" (takeBaseName path) str
replaceBaseName path name
為了更簡潔,上面的name
可以內聯計算
naming path = do
time <- getModificationTime path
replaceBaseName path
$ printf "%s_%s" (takeBaseName path)
$ formatTime defaultTimeLocale "%Y%m%d" time
我們看到我們只是在做一些純函數組合,所以我們可以使用(.)
naming path =
getModificationTime path >>= replaceBaseName path . mkTime
where
mkTime = printf "%s_%s" (takeBaseName path)
. formatTime defaultTimeLocale "%Y%m%d"
如果我們不需要getModificationTime
, replaceBaseName
和takeBaseName
path
,我們可以通過使用just (>=>)
完全刪除path
點來縮短它。 這仍然可以通過使用ReaderT
monad變換器完成,但它可能會變得更加丑陋,而不是以這個速度更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.