[英]How to check whether instance exists for a polymorphic type with Template Haskell?
假設我想檢查Show
實例是否存在類型[a]
(它確實如此)。
如果我這樣做:
let t = ListT `AppT` VarT (mkName "a")
$(stringE . show =<< isInstance ''Show [t])
我收到以下錯誤:
Not in scope: type variable `a'
In the argument of reifyInstances: GHC.Show.Show [a]
In the expression: $(stringE . show =<< isInstance ''Show [t])
In an equation for `it':
it = $(stringE . show =<< isInstance ''Show [t])
如果我那么做:
let t' = ForallT [PlainTV (mkName "a")] [ClassP ''Show [VarT (mkName "a")]] t
$(stringE . show =<< isInstance ''Show [t'])
我懂了
"False"
您實際上想在拼接之前看到這里的類型。如果您在ghci中,則可以鍵入set -ddump-splices
,它將在編譯每個拼接后打印出來。 然后,以您的示例為例:
>undefined :: $(return t)
<interactive>:27:16-23: Splicing type
return t ======> [a]
<interactive>:27:16:
Not in scope: type variable `a'
In the result of the splice:
$(return t)
>undefined :: $(return t')
<interactive>:28:16-24: Splicing type
return t' ======> forall a b. (Show a, Show b) => [a]
(第二個錯誤也給出了一個錯誤-但由於不同的原因。您可以在上下文中使用類型變量,而該變量不是實際類型。此錯誤本質上與您的問題無關)。
如您所見,類型[a]
與所有forall a . [a]
的類型都不相同forall a . [a]
forall a . [a]
。 當您寫(普通)haskell時:
func :: [a]
您實際上在寫:
func :: forall a . [a]
但是,Template Haskell不會自動為您插入forall
(這是非常不希望的行為)。 和類型
func :: forall . [a]
會給您一個錯誤( a is not in scope
錯誤),因為a
並未像人們期望的那樣綁定在任何可見范圍內! 和類型forall . [a]
forall . [a]
等效於您編寫return $ ListT
AppT VarT (mkName "a")
時TH生成的類型。
編輯:
如果您只想用具體類型替換類型var,那么實際上您只是在為您的類搜索類型。 我不知道這是否是您想要的行為,我想我正在努力尋找一個有用的案例。 但是,如果您可以輕松檢查類型上下文中是否存在類型類實例:
existentialTypeContainsClass :: Name -> Type -> Bool
existentialTypeContainsClass clss (ForallT _ cxt t) = or $ map (boundByPred clss) cxt
boundByPred :: Name -> Pred -> Bool
boundByPred _ (EqualP _ _) = False
boundByPred c (ClassP clss _) = c == clss
t = ListT `AppT` VarT (mkName "a")
t' = ForallT [PlainTV (mkName "a")] [ClassP ''Show [VarT (mkName "a")]] t
runTest = existentialTypeContainsClass ''Show t'
從GHC 7.10和Template Haskell 2.10開始, Nikita Volkov提供的原始代碼實際上是開箱即用的:
> let t = ListT `AppT` VarT (mkName "a")
> $(stringE . show =<< isInstance ''Show [t])
"True"
Nikita Volkov的答案仍然適用於GHC 7.10和TH 2.10。 這是非常優雅的恕我直言,盡管我更喜歡使用TupleT 0
代替ConT ''Int
(只是因為它是一個更簡單的類型):
> let t = ListT `AppT` TupleT 0
> $(stringE . show =<< isInstance ''Show [t])
"True"
對於GHC 7.10和TH 2.10,可以使用兩種方法中的任何一種。 對於GHC≤7.8和TH≤2.9,應使用此答案中列出的第二種方法。
為什么有問題的代碼不起作用,請參考user2407038進行解釋。
關於解決方案,盡管以下方法不能完全解決問題,但可以將其用作解決方法。 可以通過提供某些特定類型而不是變量a
來檢查[a]
。 您必須知道要檢查的類的一些特定的現有實例。 在Show
情況下,我們知道Int
存在一個實例-我們可以使用該實例:
Prelude Language.Haskell.TH> let t = ListT `AppT` ConT ''Int
Prelude Language.Haskell.TH> $(stringE . show =<< isInstance ''Show [t])
"True"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.