2012-12-30 14 views
19

我正在嘗試在Haskell中的do塊中重構mapM_函數調用。我想提取lambda到一個(本地)命名函數,使代碼更具可讀性。Haskell在do塊中的where子句語法

我的代碼最初看起來是這樣的:

do 
    -- ... 
    mapM_ (\x -> x + 1) aList 

    return aValue 

我想將其更改爲

do 
    -- ... 
    mapM_ func aList 
    where func x = x + 1 

    return aValue 

但我對return aValue線得到一個語法錯誤。我的實際lambda比較複雜:-),但我確實用這個lambda來嘗試它,以確保它不是lambda代碼中的問題。

我該如何重寫這段代碼?我應該用let ... in代替嗎?

回答

27

有三個類似(但不同)這裏定義的東西的方法:

  • 可以某些定義後附加條款where - 主要是方程式綁定。所以你可以在你的函數的末尾放置一個,或者在用let或者周圍的where子句定義的東西之後。

  • 在另一方面,let x = ... in ...表達式計算結果爲部分in後,這是唯一的地方let後的東西是可見的。

  • do塊的內部,因爲已經有一個隱式的作用域嵌套(事先在第一次定義之後就可見),所以只能單獨使用let x = ...。這與之前的表格完全相同--區塊的其餘部分實際上是in ...部分。

如果你想使用該do塊內定義一些本地定義,你唯一的選擇是第三(或通過其他值(S)作爲參數(S))。然而,對於像你的例子那樣的獨立幫助函數,任何樣式都可以。這裏是你的榜樣,證明每個:

的第一個樣式,其中func可見在foo任何地方,包括任何的where條款中的其他規定:

foo = do ... 
     mapM_ func aList 
     ... 
     return aValue 
    where func x = x + 1 

第二種風格,其中func是唯一可見的內的let表達,在這種情況下是整個do塊:

foo = let func x = x + 1 
     in do 
     ... 
     mapM_ func aList 
     ... 
     return aValue 

而第三樣式,定義它在do塊內。在這種情況下,func僅在let後纔可見;在第一個...它尚未定義。

foo = do ... 
     let func x = x + 1 
     mapM_ func aList 
     ... 
     return aValue 

哦,好措施:由於let ... in ...是一個表達式,你也可以使用它的任何地方,你有一個表達,說出一些地方的定義。因此,這裏是另一個例子:

foo = do ... 
     let func x = x + 1 in mapM_ func aList 
     ... 
     return aValue 

和以前一樣,func只是let表達,在這種情況下它後單表達,無處內可見。

+0

謝謝。第三種形式看起來像它可以讓我定義足夠接近'mapM_'的lambda函數是有用的。我只擔心它會在'let'(小問題)中定義的名稱爲'func'的頂級函數名稱空間受到污染。 – Ralph

+0

@Ralph:好吧,這只是你希望它顯示的範圍的問題。如果你的'do'塊足夠大,你需要擔心污染它裏面的命名空間,那麼你應該考慮把它分成更小的塊。 :] –

+0

是的,這也發生在我身上。我正在翻譯一些功能比它們應該更長的功能的一些功能性的Scala代碼(一些白癡寫過(:-))。 – Ralph

3

你的where不應該在函數的結尾嗎?

像這樣:

function aList aValue = do 
    mapM_ func aList 
    return aValue 
    where func x = x + 1 
+0

我不確定。我想找到一些方法將lambda提取到本地函數中以提高可讀性。如果我不得不把它放在頂層函數的末尾,那麼它就不太可用。我是Haskell noob。 – Ralph

10

另一種選擇是使用forM_而不是mapM_,它翻轉參數的順序。然後,您可以使用$運營商,其尾部的lambda表達式如下所示:

do 
    forM_ aList $ \x -> do 
    ... 

    return aValue 
+0

我沒有考慮過這個選擇。我會再看看我的代碼,看看它是否更具可讀性。在其他需要單點列表遍歷的情況下,但是沒有像'forM_'這樣的方便的翻轉版本,我可以使用'flip'來創建一個新的函數。 – Ralph