簡體   English   中英

Haskell中的運算符和函數優先級

[英]operator and function precedence in Haskell

所以這不起作用:

take 50 iterate (* 2) 1

因為它需要在第二個參數中使用括號。 我的問題是為什么。

Haskell理所當然地看到了類型的差異:

 Couldn't match expected type `[a0]'
            with actual type (a1 -> a1) -> a1 -> [a1]'

我的問題是:

1)似乎haskell首先嘗試在迭代函數之前解析take 50函數。 為什么haskell會這樣做? 在數學中,如果你有fgtwu(x),你首先評估你(x)然后再做其他事情。 為什么haskell在這種情況下開始評估f?

2)Haskell足夠聰明,可以檢測出實際類型是:

(a1 -> a1) -> a1 -> [a1]

現在,如果它看到這個函數的輸出是[a1],一個與預期類型[a0]統一的類型,為什么haskell不統一呢?

3)為什么$運營商會解決這個問題? 我知道:

($) :: (a -> b) -> a -> b

所以這個操作符的基本操作就是“寫FUNCTION $ ARGUMENT”並獲得在該參數中計算的函數的值。 在take 50案例中,類型是:

take 50 :: [a2]->[a2]
take 50 $ :: a->b 
where a ~a2 and b~b2 then
take 50 $ :: [a2]->[a2]

因此,基本上我處於與第一種情況相同的情況,不使用括號或$。 這種情況是我需要一個類型[a2]的參數(haskell稱之為[a0]但它是相同的。所以..為什么haskell統一[a2]與(a1 - > a1) - > a1 - > [a1]當我使用$但它沒有當我不使用它?

函數應用程序(用“juxtaposition”表示,將函數及其參數放在一起)必須以某種方式進行解析,並且它必須是左關聯或右關聯才能被明確地解析。 1

函數應用程序實際上是左關聯的,所以你寫的等價於

((((take 50) iterate) (* 2)) 1)

如果它是右關聯的話,你會有

(take (50 (iterate ((* 2) 1))))

這也不是你想要的,而且作為默認選擇更糟糕的是:很少有程序會讓右關聯運算符感覺更自然。

由於沒有統一的解析規則可以產生你想要的程序,所以程序員必須通過以$或一些括號的形式給出提示來告訴haskell你的實際意思。

至於為什么$ help:它被定義為在解析時具有非常低的優先級,因此寫入

take 50 $ iterate (* 2) 1

解析為

(take 50) $ ((iterate (* 2)) 1))

這實際上是你想要的。

1明確的解析是一個非常理想的屬性,用於擁有可理解的程序,讓typechecker決定如何解析事物(正如你所建議的那樣)可能會非常混亂。

fgtwu (x)也不是數學中的函數應用程序序列。 但是,在數學中,如果我們采取. 作為函數組合運算符,然后(f . g . t . w . u) (x)首先應用u函數,然后應用w函數,依此類推。 在Haskell中也是如此:( (f . g . t . w . u) x

在你的第一個類型錯誤中,它試圖將類型[a0]與類型(a1 -> a1) -> a1 -> [a1]統一,而不是類型[a1] 這是不可能統一的,所以它給出了一個錯誤。

並置是Haskell中的函數應用 ,而不是函數組合(順便說一下,數學並置通常是乘法)。 這意味着像fxyz這樣的表達式(基本上)意味着“將函數f應用於參數xyz ”。 這相當於((fx) y) z Haskell中的每個函數只需要一個參數。 這意味着像fxyz = x + y * z這樣的東西實際上與f = \\x -> \\y -> \\z -> x + y * z 函數應用程序是左關聯的,因此fxyz與寫入((fx) y) z ,即,將f應用於x首先獲取結果函數並將其應用於y 最后,將該函數應用於z

這意味着您的原始表達式被解釋為(再次轉換為標准數學符號): take(50, iterate, (* 2), 1)

take 50 iterate (* 2) 1在帶有帶括號的調用語言take (50, iterate, * 2, 1)語言中是等效的,但你想要的是take (50, iterate(* 2, 1)) $完全符合“在這里打開括號並盡可能地關閉它”的目的。 它就像調用函數一樣,但是右關聯而不是左關聯。 您可以使用take 50 (iterate (*2) 1)在沒有$運算符的情況下編寫它。

函數應用程序是左關聯的,所以

f g a b

((f g) a) b

所以你有了

(((take 50) iterate) (*2)) 1

這是不正確的,因為它將take 50 :: [a] -> [a]應用於具有類型(a -> a) -> a -> [a] iterate ,顯然不正確。

(我會給你一些更冗長的答案,涵蓋一些我認為其他答案未被發現的角落,至少沒有明確說明)。

在Haskell中, 括號表示函數調用 它們只是用於分組。 因此,在Haskell中, u (x)ux完全相同。

由哈斯克爾試圖使用生成您的首秀錯誤類型iterate的第二個參數來take

take    :: Int -> [a0]                     -> [a0]
take       50     iterate                  :: t 
iterate ::        (a1 -> a1) -> a1 -> [a1]
                  ------------------------
                  [](...) and (->)(...) do not match

它不會嘗試“在解決了take 50函數后解析iterate函數”。 它還沒有考慮你剩余的長篇大論。 它只是試圖使用iterate 這恰好是一個功能。

函數只是Haskell中的值,這就是為什么沒有特殊的語法來調用它們,就像各種foo(args){exps;}; 語言。

對於編譯器來說,嘗試糾正你的表達式太危險了:太多錯別字或錯誤會突然出現意想不到的意思。 這最多可能是一些假設的交互式開發環境的特征。

當你寫take 50 $ iterate (*2) 1 ,第二個表達式的類型(在$運算符之后)是

iterate ::        (a1 -> a1) -> a1 -> [a1]
iterate           (*2)          1  :: [Int]  -- it's actually (Num a => [a]) 
                                             -- but that's besides the point

列表的號碼類型( :: Num a => [a]是什么得到在呼叫靠在第二參數相匹配的take裸非施加的不是那種iterate本身

take    :: Int ->               [a0]       -> [a0]
take       50     (iterate (*2) 1  )       :: t 
iterate (*2) 1    :: (Num a) => [a ]
                  ------------------
                  a0 ~ (Num a)=> a            t ~ (Num a) => [a]

所以它“typechecks” ,即它有效。 事實上,在GHCi中,我們得到了

前奏>:t取50(迭代(* 2)1)
取50(迭代(* 2)1)::(Num a)=> [a]

暫無
暫無

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

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