簡體   English   中英

如何使用模板Haskell檢查是否存在多態類型的實例?

[英]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,應使用此答案中列出的第二種方法。

進一步閱讀

  • GHC門票6114 :ghc:恐慌! 使用isInstance,newName和類型splice發生;
  • GHC票證7066 :isInstance不適用於化合物類型。

為什么有問題的代碼不起作用,請參考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.

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