如果函式首先采用第一個引數,因為函式應用程式是左關聯的,例如:
drop 2 [1,2,3,4]
結果:[3,4]
相當于
(drop 2) [1,2,3,4]
相同的結果:[3,4]
這里我的問題是,如果型別簽名是右關聯的,這意味著右邊的東西首先評估,在這種情況下,它會如下所示,因為第一個引數首先由函式獲取:
drop :: [1,2,3,4] -> (2 -> [3,4])
不應該是這樣的,對吧?
drop :: Int -> ([a] -> [a])
drop :: 2 -> ([1,2,3,4] -> [3,4])
那么,為什么它在函式的型別簽名中首先采用第二個引數而不是第一個引數呢?
另外,如果第二個引數在第一個引數之前被評估,那么為什么下面的用法無效?
(drop [1,2,3,4]) 2
uj5u.com熱心網友回復:
寫諸如drop :: [1,2,3,4] -> (2 -> [3,4])或之類的東西是荒謬的2 -> ([1,2,3,4] -> [3,4])——你在那里混合了型別級別和值級別的符號。您應該做的是查看子運算式的本地型別:
drop 2 [1,2,3,4]
└─┬┘ ┊ └──┬────┘
┌─┴───────────────┐ ┌─┴─┐ ┌──┴──┐
│Int->[Int]->[Int]│ │Int│ │[Int]│
└─────────────────┘ └───┘ └─────┘
添加隱含的括號
(drop 2) [1,2,3,4]
└┬─┘ ┊ └──┬────┘
┌─┴─────────────────┐ ┌─┴─┐ ┌──┴──┐
│Int->([Int]->[Int])│ │Int│ │[Int]│
└───────────────────┘ └───┘ └─────┘
現在子運算式drop 2意味著您將引數應用為函式2的第一個引數drop,即作為Int其簽名中的。對于整個drop 2運算式,這個論點因此消失了:
( drop 2 ) [1,2,3,4]
┊ ┌──────────┴────────┐ ┌─┴─┐ ┊ └──┬────┘
┊ │Int->([Int]->[Int])│ │Int│ ┊ ┊
┊ └───────────────────┘ └───┘ ┊ ┊
└────────────┬─────────────────┘ ┌──┴──┐
┌────┴───────┐ │[Int]│
│[Int]->[Int]│ └─────┘
└────────────┘
這類似于將單引數函式length :: [Bool] -> Int應用于單個引數[False,True] :: [Bool]以獲得結果length [False,True] ≡ (2::Int)。drop結果具有型別([Int]->[Int])而不是“原子”之類的事實Int在此階段無關緊要。
然后在外部級別上,您將 type 的函式應用于 type[Int]->[Int]的引數[Int],這是非常明智的。然后整個事情只有 result type [Int]。
( ( drop 2 ) [1,2,3,4] )
┊ ┊ ┌──────────┴────────┐ ┌─┴─┐ ┊ └──┬────┘ ┊
┊ ┊ │Int->([Int]->[Int])│ │Int│ ┊ ┊ ┊
┊ ┊ └───────────────────┘ └───┘ ┊ ┊ ┊
┊ └────────────┬─────────────────┘ ┊ ┊
┊ ┌────┴───────┐ ┌──┴──┐ ┊
┊ │[Int]->[Int]│ │[Int]│ ┊
┊ └────────────┘ └─────┘ ┊
└────────────────────────┬────────────────────┘
┌──┴──┐
│[Int]│
└─────┘
uj5u.com熱心網友回復:
我認為您誤解了右聯想的含義。這確實意味著:
drop :: Int -> [a] -> [a]
相當于:
drop :: Int -> ([a] -> [a])
因此,這意味著這drop是一個函式,它接受一個型別的引數Int,然后回傳一個型別的函式[a] -> [a]。
但是函式應用程式本身是左關聯的。確實:
drop 2 [1,2,3,4]
簡稱:
(drop 2) [1,2,3,4]
因此,此處drop 2將回傳一個型別的函式,該函式[a] -> [a]將洗掉串列的前兩項。然后我們應用[1,2,3,4]到那個函式,從而得到[3,4].
uj5u.com熱心網友回復:
我想我現在已經理解了這個難題:
在函式應用程式中,函式將始終從左到右,一個接一個地獲取引數,因為它是左關聯的。其目的是用引數替換系結變數。
現在,當涉及到函式的型別簽名時,它確實是右關聯的,但這并不意味著該函式將通過獲取最后一個引數,然后是倒數第二個引數,等等來應用。
一個函式應用:
((((a) b) c) d) e
表示以下運算式具有以下型別:
a :: b -> (c -> (d -> e))
a b :: c -> (d -> e)
(a b) c :: d -> e
((a b) c) d :: e
這可以讀作:函式接受第一個引數 (b) 并回傳第二個函式 (c -> (d -> e)),它接受第二個引數 (c) 并回傳第三個函式 (d -> e)接受第三個引數 (d) 并回傳最終結果 (e)。
正如@Daniel Wagner 指出的那樣:
關聯順序并不意味著函式的評估順序。
在這種情況下,型別簽名中的右關聯等價如下,一對括號 (c -> d) 表示從左側獲取 (b) 的函式回傳的函式:
a :: b -> (c -> d)
由于 Haskell 中的函式是基于 Lambda 演算的,所以在這里我將使用 lambdas,因為我對它們很熟悉,例如:
原始版本:
(λab.a b) (1,2)
= (a b)[a:=1,b:=2]
= 1 2
= 3
但是由于 Lambda 演算中的抽象在每一步中只需要一個輸入,因此可以將其重寫為:
咖喱版:
(λa.(λb.a b)) 1 2
= ((λb.a b)[a:=1]) 2
= (λb.1 b) 2
= (1 b)[b:=2]
= 1 2
= 3
注意:這篇文章的術語或邏輯可能不正確。如果您能夠并希望,您可以編輯我的帖子以更正它。:)
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/474034.html
