[英]Why does the Maybe return type make this crash?
我限制自己使用預內置函數進行培訓。 我已將長度重新編碼為計數並且有效。
我有一個搜索功能,當給定索引和列表時,它只返回列表中索引處的值。 它工作得很好。 當索引太大時會拋出錯誤。
search [] _ = error "index too large"
search (a:_) 0 = a
search (_:a) b = search a (b - 1)
現在,我想要一個safeSearch
function 如果索引太大或者列表為空則返回Nothing
。 所以我只是這樣做了。
safeSearch :: [a] -> Int -> Maybe a
safeSearch a b
| b < 0 || b >= count a = Nothing
| otherwise = Just (search a b)
它有效。 ...只要您不在空列表上嘗試。 即使索引對於列表長度來說太大了。
main = print(safeSearch [] 5)
這崩潰了,我真的找不到任何解決方法。
盡管我認為我的第二行沒有用(因為如果列表為空,它的計數為 0,所以我們放入第一個守衛並且它應該返回 Nothing?)它不起作用。 刪除它並不能解決問題。
這是編譯時錯誤。
main.hs:91:8: error:
* Ambiguous type variable `a0' arising from a use of `print'
prevents the constraint `(Show a0)' from being solved.
Probable fix: use a type annotation to specify what `a0' should be.
These potential instances exist:
instance Show Ordering -- Defined in `GHC.Show'
instance Show Integer -- Defined in `GHC.Show'
instance Show a => Show (Maybe a) -- Defined in `GHC.Show'
...plus 22 others
...plus 13 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
* In the expression: print (safeSearch [] 5)
In an equation for `main': main = print (safeSearch [] 5)
|
91 | main = print(safeSearch [] 5)
| ^^^^^^^^^^^^^^^^^^^^^^
exit status 1
任何想法? 我錯過了什么,甚至完全出錯了? 一個我需要更深入理解的概念?
問題是編譯錯誤。 這意味着它實際上並沒有運行您的代碼並觸發了您的error "index too large"
調用; 編譯器甚至在嘗試運行它之前就拒絕了您的代碼。 因此,如果您試圖更改代碼以避免出現這種情況,那么您找錯地方了。
實際發生的是safeSearch [] 5
返回一個Maybe a
類型的值,其中a
是列表中元素的類型。 但是您沒有在列表中包含任何元素,因此根本無法確定a
是什么類型。
您的 function safeSearch
可以用於任何類型,所以這實際上很好。 但是您還嘗試print
Maybe a
值。 使用print
需要一個Show
實例,而Maybe a
的實例還需要a
Show
實例。 因為沒有說明a
是什么類型,所以編譯器無法為它找到合適的Show
實例,所以它不得不中止編譯並報錯。
解決它的最直接的方法是添加一個類型注釋(列表或Maybe a
safeSearch
產生的值)。 是這樣的:
main = print (safeSearch ([] :: [Int]) 5)
(這就是錯誤消息所說的,當它說一個不明確的類型變量正在阻止解決Show
約束時,可能的解決方法是添加一個類型注釋)
請注意,此類問題在“真實”代碼中很少出現。 通常,如果您將列表處理成具有相關類型的另一個結構,您將有其他代碼對元素或結果執行某些操作,或者生成列表(並不總是空的)。 除了這些類型的快速測試之外,您通常不會編寫一個只處理始終為空的列表並打印結果的程序。 所以通常情況下,當還有其他代碼時,編譯器將有足夠的上下文來推斷空列表的類型,並且不需要額外的類型注釋。 所以這種額外的類型注釋通常不被認為是需要避免的嚴重負擔,因為在“真實”代碼中幾乎不需要它們。 您只需按照自己的意願進行編碼,並且在編譯錯誤的情況下讓您意識到您需要一個注釋,您只需添加它並繼續。
如果您在 GHCi 中進行這種快速檢查而不是使用main
function 編寫完整程序,那么您也不需要額外的類型注釋。 這是因為 GHCi 默認開啟了ExtendedDefaultRules
語言擴展。 “默認規則”是 GHC 為您選擇類型而不是拋出“模糊類型”錯誤的條件。 正常的默認規則非常嚴格,實際上僅設計用於默認數字約束(如Num a
或Real a
等)。 它們不適用於您的原始示例。 “擴展默認規則”更頻繁地應用以避免在交互式解釋器中需要大量類型簽名(因為您一次輸入一行,而不是編譯器能夠看到完整的模塊以從使用中推斷類型)。 在這種情況下,在解釋器提示符下輸入print (safeSearch [] 5)
將起作用,因為它將返回的類型默認為Maybe ()
,並且恰好打印Nothing:: Maybe ()
產生相同的 output ,如果它會正確地猜到了你真正想要的類型。
但在幾乎所有實際程序中,將類型變量默認為()
將是一件愚蠢的事情,它會使事情變得更少,所以我不建議養成在實際模塊中啟用ExtendedDefaultRules
的習慣。 只需添加類型注釋,或在解釋器中而不是在模塊中進行快速檢查。
您編寫的內容適用於任何真實世界的用例。 只有當有人寫print (safeSearch [] x)
時它才會失敗——一個文字空列表,沒有上下文來說明預期的結果類型。 如果他們傳入一個非空列表,或者一個恰好計算為空列表的列表表達式,或者如果他們以一種讓類型推斷確定其意圖的方式使用結果,它就可以正常工作。
此外,確實沒有辦法編寫 function 以便在傳遞無上下文空列表時它可以工作。 使類型清晰的負擔必須放在調用站點上,而不是定義上。 對您問題的評論已經說明了如何執行此操作; 當你以一種顯然無用的方式調用你的 function 時,你只需要那么明確。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.