是否有類似 Show(派生 Show)的東西只使用代數資料型別的建構式?(請不要介意我使用的是建構式這個詞,我不知道正確的名稱......)
這個問題的原因是,對于我的許多代數資料型別,我不想費心讓它們的內容也派生 Show,但我仍然想獲得一些關于使用的建構式的除錯資訊,而不必實作顯示每個建構式。 .
另一種方法是給我建構式名稱的函式,我可以在我自己的 show 實作中使用它。
這當然需要做一些編譯器魔法(自動派生),因為背后的整個想法是不必顯式地實作每個資料建構式字串表示。
uj5u.com熱心網友回復:
對于有Data.Data.Data實體的型別,這個函式很簡單:它只是
showConstr . toConstr :: Data a => a -> String
例如,
Prelude Data.Data> showConstr . toConstr $ Just 5
"Just"
對于沒有實作的型別Data,它是相當絕望的,因為你無法查看型別內部以了解它是如何實作的。但是由于您自己定義了這些型別,您只能確保它們具有 Data 實體。它是自動派生的deriving Data,前提是您已啟用DeriveDataTypeable。
請注意,Data它僅適用于代數和透明的型別。您將無法派生一個型別的實體,該型別在其欄位之一中包含例如函式。所以這可能不會像你希望的那樣從 Show 的暴政中得到緩解:很多 Show 無法支持的型別也會被 Data 拒絕。Generic 可以提供更通用的解決方案。我不是泛型專家,但conNameOf看起來很有希望。
uj5u.com熱心網友回復:
更明確的方法是通過TemplateHaskell創建自定義派生。以下代碼描述了Show為給定資料型別生成自定義實體的邏輯:
import Language.Haskell.TH
mk_constr_show :: Name -> Q [Dec]
mk_constr_show typeName =
do -- Getting type definition
(TyConI d) <- reify typeName
-- Extracting interesting info: type name and the constructors
(type_name, constructors) <-
-- Our code should work for both `data` and `newtype`
case d of
d@(DataD _ name _ _ cs _) ->
return (name, map (\(NormalC cname args) -> (cname, length args)) cs)
d@(NewtypeD _ name _ _ (NormalC cname args) _) ->
return (name, [(cname, length args)])
_ -> error ("derive: not a data type declaration: " show d)
-- Manually building AST for an instance.
-- Essentially, we match on every constructor and make our `show`
-- return it as a string result.
i_dec <- instanceD (cxt [])
(appT (conT (mkName "Show")) (conT type_name))
[funD (mkName "show") (flip map constructors $ \constr ->
let myArgs = [conP (fst constr) $ map (const wildP) [1..snd constr]]
myBody = normalB $ stringE $ nameBase $ fst constr
in clause myArgs myBody []
)]
return [i_dec]
然后,您只需執行
data MyData = D Int | X
$(mk_constr_show ''MyData)
......你可以很高興show。請注意,兩個代碼片段必須放在單獨的模塊中,并且您需要使用TemplateHaskell擴展。
我從這篇文章中獲得了很多靈感。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/415119.html
標籤:
