我正在閱讀Thinking with Types,第 4 章有一個問題:
type family AlwaysUnit a where
AlwaysUnit a = ()
{- which a is ambiguous type? -}
AlwaysUnit a -> a
b -> AlwaysUnit a -> b
Show a => AlwaysUnit a -> String
我知道Show a => AlwaysUnit a -> String有歧義型別,但我想知道為什么AlwaysUnit a -> a沒有歧義型別?我無法AlwaysUnit a -> a將此型別簽名實作到函式中。
uj5u.com熱心網友回復:
type別名(以及type family擴展別名)不會創建新型別。它們實際上是右側事物的別名。在您的情況下, 中只有一個分支type family,并且它匹配所有型別,因此將始終采用它。所以AlwaysUnit a總是等于。_ ()不是同構的,不是類似的,而是相等的。也就是這三種型別
AlwaysUnit a -> a
b -> AlwaysUnit a -> b
Show a => AlwaysUnit a -> String
與這三個完全相等(同樣,不是同構,而是一種非常強的相等形式)
() -> a
b -> () -> b
Show a => () -> String
在第一個例子中,我們仍然有一個a,所以它不是模棱兩可的。在第二個例子中,a完全消除了,所以就好像它一開始就不存在一樣。Haskell 很樂意悄悄地忽略“未使用”變數。然而,在第三個示例中,我們有一個a在約束中使用的變數(在 左邊的東西=>),但不是在正確的型別中。每當我們嘗試呼叫該函式時,都會產生問題,而且TypeApplications我認為沒有解決該沖突的好方法。
AlwaysUnit請注意,如果有多個案例或對其型別進行一些模式匹配,事情會更加復雜。您最終可能會遇到所有案例都不匹配或 GHC 無法決定案例是否應該匹配的情況,并且它將拒絕簡化。在這種情況下,你最終會得到一個AlwaysUnit a不會簡化你的型別的。
我無法
AlwaysUnit a -> a將此型別簽名實作到函式中。
這是一個很好的直覺,但有幾個問題。首先,你總是可以在 Haskell 中實作任何功能。
f :: AlwaysUnit a -> a
f _ = undefined
因此,如果我們允許底部型別,那么 Haskell 中的每種型別都會被占用。即使我們不允許底部型別,那么“我可以實作這個函式”和“型別是否模糊”是完全不同的問題。例如,如果Int -> Void不求助于底層型別,我就無法實作型別的功能,因為Int它顯然是有人居住的,Void而顯然不是。但它不是一個模棱兩可的型別。這是一個完美定義但為空的函式型別。
uj5u.com熱心網友回復:
當然,你不能實作AlwaysUnit a -> awhena是一個未系結的型別變數(不使用 ⊥ 作弊)。但這不是問題要問的。模棱兩可的型別在問,如果編譯器遇到這種型別的運算式,它可以a從背景關系中推斷出一個型別嗎?
假設我們有
exercise1 :: AlwaysUnit a -> a
exercise1 = undefined
main = putStrLn $ exercise1 ()
你能說出a在這個呼叫站點選擇了什么型別嗎?是的,因為putStrLn要求 a String,我們必須有a ~ String。因此,a定義中的型別變數exercise1是沒有歧義的。
對于其他兩個練習,沒有背景關系可以讓您將這些型別放入其中來推斷a. 在練習 2 中,這很好 - 沒有人需要知道a使用該功能是什么。但是在練習 3 中,a需要解決 Show 約束,所以它是模棱兩可的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/517172.html
標籤:哈斯克尔
