[英]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
關鍵字應該做什么,因為那沒用。
我理解類型類是開放的,所以只是因為你可以在上面引用的上下文中做出假設, Word16
是C
的唯一數字實例,可能在其他模塊中不能容納。 但我的問題是:是否有一些機制可以假設/強制執行該屬性,因此可以使用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.