簡體   English   中英

是否有(返回a)>> = b的標准Haskell函數?

[英]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 >>= ffx相同---換句話說,你的大部分內部函數都是純函數,根本不需要(>>=) 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"

如果我們不需要getModificationTimereplaceBaseNametakeBaseName path ,我們可以通過使用just (>=>)完全刪除path點來縮短它。 這仍然可以通過使用ReaderT monad變換器完成,但它可能會變得更加丑陋,而不是以這個速度更好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM