簡體   English   中英

為什么 Maybe 返回類型會導致崩潰?

[英]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 aReal a等)。 它們不適用於您的原始示例。 “擴展默認規則”更頻繁地應用以避免在交互式解釋器中需要大量類型簽名(因為您一次輸入一行,而不是編譯器能夠看到完整的模塊以從使用中推斷類型)。 在這種情況下,在解釋器提示符下輸入print (safeSearch [] 5)將起作用,因為它將返回的類型默認為Maybe () ,並且恰好打印Nothing:: Maybe ()產生相同的 output ,如果它會正確地猜到了你真正想要的類型。

但在幾乎所有實際程序中,將類型變量默認為()將是一件愚蠢的事情,它會使事情變得更少,所以我不建議養成在實際模塊中啟用ExtendedDefaultRules的習慣。 只需添加類型注釋,或在解釋器中而不是在模塊中進行快速檢查。

您編寫的內容適用於任何真實世界的用例。 只有當有人寫print (safeSearch [] x)時它才會失敗——一個文字空列表,沒有上下文來說明預期的結果類型。 如果他們傳入一個非空列表,或者一個恰好計算為空列表的列表表達式,或者如果他們以一種讓類型推斷確定其意圖的方式使用結果,它就可以正常工作。

此外,確實沒有辦法編寫 function 以便在傳遞無上下文空列表時它可以工作。 使類型清晰的負擔必須放在調用站點上,而不是定義上。 對您問題的評論已經說明了如何執行此操作; 當你以一種顯然無用的方式調用你的 function 時,你只需要那么明確。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM