簡體   English   中英

Haskell 是否“理解”柯里化函數定義?

[英]Does Haskell “understand” curried function definitions?

在 Haskell 中,函數總是采用一個參數。 多個參數通過Currying實現。 既然如此,我可以在下面看到如何將具有兩個參數的函數定義為“func1”。 它是一個返回函數(閉包)的函數,該函數將外部函數的單個參數添加到返回函數的單個參數中。

然而,盡管這就是柯里化函數的工作方式,但這不是用於定義雙參數函數的常規 Haskell 語法。 相反,我們被教導定義像“func2”這樣的函數。

我想知道 Haskell 如何理解 func2 應該與 func1 的行為方式相同。 func2 的定義並沒有告訴我它是一個返回函數的函數。 相反,它實際上看起來像一個雙參數函數,我們被告知不存在的東西!

這里有什么訣竅? Haskell 是不是剛出生就知道我們可以用這種教科書的方式定義多參數函數,並且它們無論如何都以我們期望的方式工作? 也就是說,這是一個似乎沒有明確記錄的語法約定(Haskell 知道你的意思並且會為你提供丟失的函數返回),或者是否有其他一些魔法在起作用或我遺漏了什么?

func1 :: Int -> (Int -> Int)
func1 x = (\y -> x + y)

func2 :: Int -> Int -> Int
func2 x y = x + y

main = do
    print (func1 7 9)
    print (func2 7 9)

語言本身中,編寫形式為fxyz = _的函數定義相當於f = \\xyz -> _ ,相當於f = \\x -> \\y -> \\z -> _ 這沒有理論上的原因。 只是那些嵌套的 lambda 抽象是一個可怕的眼睛/手指疼痛,每個人都認為犧牲一點迂腐來為它制作一些語法糖是可以的。 這就是表面上的全部內容,現在可能就是您需要知道的全部內容。

然而,在語言的實現中,事情變得更加棘手。 在最常見的 GHC 實現中, fxy = _f = \\x -> \\y -> _之間實際上存在差異。 當 GHC 編譯 Haskell 時,它會為聲明分配arity f的前一個定義的元數為2 ,后者的元數為0 GHC.Base獲取(.)

(.) f g = \x -> f (g x)

(.)具有 arity 2 ,即使它的類型 ( (b -> c) -> (a -> b) -> a -> c ) 表示它最多可以應用三次。 這會影響優化:GHC 只會內聯一個已飽和的函數,或者應用的參數至少與其元數一樣多。 在調用(maximum .)(.)不會內聯,因為它只有一個參數(它是不飽和的)。 在呼叫(maximum . f)內聯到\\x -> maximum (fx)和在(maximum . f) 1中, (.)將直列第一到的λ抽象(產生(\\x -> maximum (fx)) 1 ),這將 beta 減少到maximum (f 1) 如果(.)被執行

(.) f g x = f (g x)

(.)將具有 arity 3 ,這意味着它不會經常內聯(特別是f . g情況,這是高階函數的一個非常常見的參數),可能會降低性能,這正是它的評論所說的:

確保它只在左側有兩個參數,以便在應用於兩個函數時內聯,即使沒有最終參數

最終答案:根據語言的語義,這兩種形式應該是等效的,但是在 GHC 中,這兩種形式在優化方面具有不同的特征,即使它們總是給出相同的結果。

在談論類型簽名時,沒有“多參數函數”這樣的東西。 所有函數都是單參數,周期。 Haskell 不需要以某種方式將多參數函數“翻譯”為單參數函數,因為前者根本不存在。

所有函數類型簽名看起來都像a -> b ,其中a是參數類型, b是返回類型。 有時b可能恰好包含更多箭頭-> ,在這種情況下,我們人類(但不是編譯器)可能會說該函數有多個參數。

在談論實現的語法時,即fxy = z - 這只是語法糖,它在編譯期間被脫糖(即機械轉換)為f = \\x -> \\y -> z

暫無
暫無

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

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