[英]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
應用於參數x
, y
和z
”。 這相當於((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.