我想將 JSON 檔案決議為資料型別,并在決議錯誤的情況下列印錯誤以及預期的資料結構。
我們以資料記錄為例:
data Foo = {
bar :: String
}
當無法決議時,我想將此資料的結構列印到控制臺中,以向用戶顯示 JSON 應該是什么結構。另外,我不想將結構硬編碼到錯誤日志中,以使其型別安全(意思是:當我向資料添加新欄位時,也應該列印它以防決議錯誤。當它會硬編碼我會忘記修改有關要列印的結構的字串)。
當我獲得此資料的值時,只需deriving Show將Foo. 但是如何列印沒有值的結構呢?
如果我能夠逐字列印型別定義就足夠了。但是,如果您有一個可以很好地格式化的型別安全解決方案,那么它肯定是受歡迎的 :)
uj5u.com熱心網友回復:
我確信有多種方法可以做到這一點,但我使用了泛型。如果你不熟悉泛型,你應該閱讀GHC.Generics的檔案。
首先,一些語言擴展和匯入:
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeOperators #-}
import GHC.Generics
import GHC.TypeLits ( KnownSymbol, symbolVal )
import Data.Proxy ( Proxy(..) )
import Data.Typeable (typeRep, Typeable)
使用DefaultSignatures擴展和DeriveGeneric,我們可以創建一個PrintStructure自動printStructure為您實作功能的型別類。您需要做的就是傳入Proxy帶有型別幻象變數WhateverYouWantToPrint的 a 。
class PrintStructure a where
printStructure :: Proxy a -> String
default printStructure :: (Generic a, PrintStructure' (Rep a)) => Proxy a -> String
printStructure _ = printStructure' (Proxy :: Proxy (Rep a p))
PrintStucture'是一個使用它們的表示來列印型別結構的類Generic。
class PrintStructure' a where
printStructure' :: Proxy (a p) -> String
現在,除非您已閱讀檔案,否則下一部分將毫無意義,因此請先閱讀。(泛型并不像看起來那么復雜)。
實作PrintStructure'void 和 unit 型別:
-- Empty string, there's nothing to print for them
instance PrintStructure' V1 where
printStructure' _ = ""
instance PrintStructure' U1 where
printStructure' _ = ""
Constructors:
instance ( PrintStructure' f
, KnownSymbol name
) => PrintStructure' (C1 ('MetaCons name fx sl) f) where
printStructure' _ = symbolVal (Proxy @name)
" { "
printStructure' (Proxy :: Proxy (f p))
" }"
Record selectors:
instance ( KnownSymbol name
, PrintStructure' f
) => PrintStructure' (S1 ('MetaSel ('Just name) su ss ds) f) where
printStructure' _ = symbolVal (Proxy @name) " :: " printStructure' (Proxy :: Proxy (f p))
Sums, products, constructor fields and data types:
instance ( PrintStructure' f
, PrintStructure' g
) => PrintStructure' (f :*: g) where
printStructure' _ = printStructure' (Proxy :: Proxy (f p))
", "
printStructure' (Proxy :: Proxy (g p))
instance ( PrintStructure' f
, PrintStructure' g
) => PrintStructure' (f : : g) where
printStructure' _ = printStructure' (Proxy :: Proxy (f p))
" | "
printStructure' (Proxy :: Proxy (g p))
instance ( PrintStructure' f
) => PrintStructure' (S1 ('MetaSel 'Nothing su ss ds) f) where
printStructure' _ = printStructure' (Proxy :: Proxy (f p))
instance (Typeable t) => PrintStructure' (Rec0 t) where
printStructure' _ = show (typeRep (Proxy @t))
instance ( KnownSymbol name
, PrintStructure' f
) => PrintStructure' (D1 ('MetaData name mod pkg nt) f) where
printStructure' _ = "data "
symbolVal (Proxy @name)
" = "
printStructure' (Proxy :: Proxy (f p))
Now we can test it out.
λ> data Foo = Bar { p :: String, q :: Int } | Baz { r :: Bool } | Qux () Int deriving (Show, Generic)
λ> instance PrintStructure Foo
λ> putStrLn $ printStructure (Proxy :: Proxy Foo)
data Foo = Bar { p :: [Char], q :: Int } | Baz { r :: Bool } | Qux { (), Int }
And it works! As you can see, the formatting isn't the best, but pretty printing shouldn't be much of a problem.
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/427292.html
標籤:哈斯克尔
