我正在做一個Haskell類型的練習,這個難題讓我難堪。所提供的表達式爲:瞭解這個表達式的Haskell類型
f2 f g h = h.g.f
而對於f2的類型是顯然:
f2 :: (a -> b1) -> (b1 -> b) -> (b -> c) -> a -> c
這似乎對於這樣的短表達過於複雜。有人可以解釋爲什麼這種類型有意義嗎?
我正在做一個Haskell類型的練習,這個難題讓我難堪。所提供的表達式爲:瞭解這個表達式的Haskell類型
f2 f g h = h.g.f
而對於f2的類型是顯然:
f2 :: (a -> b1) -> (b1 -> b) -> (b -> c) -> a -> c
這似乎對於這樣的短表達過於複雜。有人可以解釋爲什麼這種類型有意義嗎?
你爲什麼覺得這很複雜?它只是一個接受三個函數(匹配類型)並返回一個新函數的函數。
我已將b1
更名爲b
並更新了其他名稱以保持一致性。因此,這裏是一個稍微修改版本:
f2 :: (a -> b) -> (b -> c) -> (c -> d) -> a -> d
f2
需要f
其類型a -> b
。意思是說,它是一個函數,用一些類型的輸入a
(a
可以是任何東西,這裏沒有限制)和一些返回值類型b
。f2
需要g
哪個輸入的類型必須匹配f
的輸出,所以它是b -> c
。它不可能像e -> c
(其中e
與b
不同),或者代碼無效,因爲它不可能組成f . g
。h
與c -> d
類型完全相同。f2
返回的結果)是一個新函數,它接受任何f
並返回任何h
返回值,因此它是a -> d
。有了這個,我們已經涵蓋了整個類型定義。基本上,這是一個相對較長的定義,但我認爲它是一個非常簡單的本質。
我發現這些「確定這樣和那樣的表達的類型」通常有點倒退。類型應該總是優先:你想爲某個任務編寫一個解決方案,你將問題描述作爲一個類型簽名。 然後你繼續前進,實際編寫一個實現。
在這種情況下,你的問題開始:我有三個功能f
,g
和h
,和類型的值,我可以傳遞給f
。此外,函數具有成對匹配的結果/參數類型。因此,簽名
f2 :: (α -> β) -> (β -> γ) -> (γ -> δ) -> α -> δ
現在你可以去和明確的尖形式實現這一點,即
f2 f g h x = h (g (f x))
這仍然是相當短暫的。畢竟,這是一項非常簡單的任務!
但是在Haskell中,通過使用標準組合運算符.
可以使它更短。最終實施非常短的事實基本上是由於f2
與.
基本相同,只是兩次。所以這並不比使用複雜類型簽名的非常複雜的任務更令人驚訝,但是發現一些包含幾乎完成任務的函數的庫。很顯然,調用這個ready-build函數會比任務複雜度提供的實現要短得多,但複雜性只能推遲到庫函數。
我和你在同一個問題上等同於(b - > c),但我不明白爲什麼類型表達式不僅僅在那裏結束。爲什麼我需要接下來的兩個,就我而言 - > a - > c? –
@KaiD因爲類型表達式覆蓋了返回值。 'a - > c'('α - >δ')是'f2'返回的一種類型。 – drdaeman
第四點是什麼讓我困惑,我理解到那裏。具體而言,我不會得到「接受任何回報,並返回任何回報」, –
好吧,我盯着它看了一會兒,我現在明白了爲什麼我給出的例子中的d或c會在那裏,但我是仍然有些困惑,爲什麼必須在那裏。 –
@KaiD:考慮一個稍微簡單的例子'f3 f g = f。 g',其類型爲'f3 ::(a - > b) - >(b - > c) - > a - > c'。它有兩個函數('f'和'g'),併產生一個新函數。在'a - > c'部分,它表示生成的函數的輸入類型將是'a' - 與'f'相同的類型。返回類型是'd',與'g '回報。例如,如果'f'接受一個字符串並返回一個整數,'g'接受一個整數並返回一個布爾值,那麼'f3'的結果必須接受一個字符串並返回一個布爾值 - 這就是類型聲明所說的。說得通? – drdaeman