簡體   English   中英

使用可用的類實例解決類型歧義

[英]Resolving type ambiguities using available class instances

給出以下代碼:

import Data.Word

data T = T deriving (Eq, Show)

class    C a      where f :: a -> ()
instance C T      where f _ = ()
instance C Word16 where f _ = ()

main = return $ f 0x16

GHC抱怨它無法推斷文字0x16的類型應該與錯誤:

No instance for (Num a0) arising from the literal ‘22’
The type variable ‘a0’ is ambiguous

很容易理解為什么會這樣--Haskell允許數字文字屬於任何具有Num實例的類型,在這里我們不能消除文字0x16 (或22)應該是什么類型的歧義。

它也清楚地表達了我想要做的人類 - 只有一個C類的可用實例滿足Num約束,所以顯然我打算使用那個,所以0x16應該被視為Word16

我知道有兩種方法可以修復它:使用其類型注釋文字:

main = return $ f (0x16 :: Word16)

或者定義一個基本上為您做注釋的函數:

w16 x = x :: Word16
main = return $ f (w16 0x16)

我嘗試了第三種方法,在文件的頂部default (Word16) ,希望Haskell選擇它作為數字文字的默認類型,但我想我誤解了default關鍵字應該做什么,因為那沒用。

我理解類型類是開放的,所以只是因為你可以在上面引用的上下文中做出假設, Word16C的唯一數字實例,可能在其他模塊中不能容納。 但我的問題是:是否有一些機制可以假設/強制執行該屬性,因此可以使用f並讓Haskell將其數字參數的類型解析為Word16而無需在調用站點進行顯式注釋?

上下文是我正在實現一個EDSL,當我知道我的參數將是Word16或其他非數字類型時,我寧願不必包含手動類型提示。 如果它讓EDSL感覺更自然,我會對一些骯臟的類型/擴展濫用開放! 雖然如果解決方案確實涉及頑皮的pragma,我肯定會欣賞在使用它們時我應該警惕的暗示。

使用GHC 7.10的“淘氣的pragma”快速解決方案:

{-# LANGUAGE TypeFamilies, FlexibleInstances #-}

class    C a where f :: a -> ()
instance C T where f _ = ()
instance {-# INCOHERENT #-} (w ~ Word16) => C w where f _ = ()

並使用GHC 7.8:

{-# LANGUAGE TypeFamilies, FlexibleInstances, IncoherentInstances #-}

class    C a where f :: a -> ()
instance C T where f _ = ()
instance (w ~ Word16) => C w where f _ = ()

在這里,GHC基本上選擇了在嘗試統一實例頭部和約束之后剩余的任意最具體的實例。

你應該只使用這個

  • 您有一組固定的實例,不會導出該類。
  • 對於類方法的所有用例,有一個可能的最具體的實例(給定約束)。

很多人建議不要使用 IncoherentInstances ,但我認為這可能是很有趣的DSL-S,如果我們觀察了上述事項。

對於其他任何想知道default (我知道我是!)

https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-750004.3

引用部分4.3.4:

在發現模糊類型的情況下,如果出現以下情況,則模糊類型變量v是可以違約的:

  • v僅出現在C v形式的約束中,其中C是一個類,並且
  • 這些類中至少有一個是數字類(即Num或Num的子類),和
  • 所有這些類都在Prelude或標准庫中定義。

這就解釋了為什么你的default子句被完全忽略了; C不是標准庫類型類。

(至於為什么這是規則......無法幫助你。大概是為了避免破壞任意用戶定義的代碼。)

暫無
暫無

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

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