2017-07-15 77 views
7

是否有寫如下的方式:有沒有辦法縮短這個派生子句?

{-# LANGUAGE DeriveDataTypeable #-} 
{-# LANGUAGE DeriveAnyClass  #-} 

data X = A | B | C 
    deriving (Eq, Ord, Show, Read, Data, SymWord, HasKind, SMTValue) 

從而使deriving子句可以以某種方式縮短,爲類似以下內容:

data X = A | B | C deriving MyOwnClass 

我想避免TH如果在所有可能的,我很高興創建一個新的類,必要時將所有派生類作爲其超類(如上面的MyOwnClass),但這並不適用於deriving機制。隨着約束種擴展,我發現,你可以這樣寫:

type MyOwnClass a = (Eq a, Ord a, Show a, Read a, Data a, SymWord a, HasKind a, SMTValue a) 

不幸的是,我不能忍受的是,deriving子句。有沒有什麼神奇的做法呢?

編輯從評論看來,TH可能是唯一可行的選擇。 (CPP宏真的不行!)如果是這樣的話,TH解決方案的草圖將很好看。

+3

'derivation'從句實際上是魔術。沒有TH,恐怕沒有辦法做到你想做的事。 –

+2

考慮在https://github.com/haskell/rfcs中製作RFC – baxbaxwalanuksiwe

回答

5

還有不好和容易這樣做的方法和不錯,但很難的方式。由於Silvio Mayolo表示可以使用TemplateHaskell來編寫這樣的功能。這種方式很難而且相當複雜。更簡單的方法是使用C-預處理是這樣的:

{-# LANGUAGE CPP #-} 

#define MY_OWN_CLASS (Eq, Ord, Show, Read, Data, SymWord, HasKind, SMTValue) 

data X = A | B | C 
    deriving MY_OWN_CLASS 

更新(2016年7月17日):想法&草圖的TH解決方案

推出的解決方案草圖之前,我將說明爲什麼這是很難與TH做。 deriving - 條款不是一些獨立的條款,它是data聲明的一部分,因此不能僅編碼deriving中的部分內容。編寫任何TH代碼的一般方法是在括號中使用runQ命令,以查看最後應寫入的內容。就像這樣:

ghci> :set -XTemplateHaskell 
ghci> :set -XQuasiQuotes 
ghci> import Language.Haskell.TH 
ghci> runQ [d|data A = B deriving (Eq, Show)|] 
[ DataD 
    [] 
    A_0 
    [] 
    Nothing 
    [ NormalC B_1 [] ] 
    [ ConT GHC.Classes.Eq , ConT GHC.Show.Show ] 
] 

現在你看到這種類型的類deriving被指定爲DataD最後一個參數 - 數據聲明 - 構造。解決您的問題的方法是使用-XStadandaloneDeriving extension。這就像deriving,但很強大,但也很詳細。再次,看,正是你想要生成什麼,只是用runQ

ghci> data D = T 
ghci> :set -XStandaloneDeriving 
ghci> runQ [d| deriving instance Show D |] 
[ StandaloneDerivD [] (AppT (ConT GHC.Show.Show) (ConT Ghci5.D)) ] 

您可以直接使用StandaloneDerivD和其他的構造或只使用[d|...|] -brackets雖然他們有更多的魔術,而是他們給你的Dec列表(聲明)。如果你想生成若干聲明的,那麼你應該寫你的功能是這樣的:

{-# LANGUAGE TemplateHaskell #-} 
{-# LANGUAGE QuasiQuotes  #-} 
{-# LANGUAGE StandaloneDeriving #-} 

module Deriving where 

import Language.Haskell.TH 

boilerplateAnnigilator :: Name -> Q [Dec] 
boilerplateAnnigilator typeName = do 
    let typeCon = conT typeName 
    [d|deriving instance Show $(typeCon) 
     deriving instance Eq $(typeCon) 
     deriving instance Ord $(typeCon) 
     |] 

簡短的教程can be found here

然後你就可以在其他文件中使用它(這是所謂的上演限制 TH限制:你應該定義在一個文件中宏觀,但你不能在同一個文件中使用它)是這樣的:

{-# LANGUAGE StandaloneDeriving #-} 
{-# LANGUAGE TemplateHaskell #-} 

import Deriving 

data X = A | B | C 

boilerplateAnnigilator ''X 

你應該在boilerplateAnnigilator函數中放入你想要的其他類型的類。但是這種方法僅適用於非參數化類。如果你有data MyData a = ...然後獨立推導應該是這樣的:

deriving instance Eq a => MyData a 

如果你想爲參數化類的TH微距爲好,那麼你基本上應該能夠通過推斷類型是否具有類型實現GHC編譯器的整個邏輯變量或不變,並根據它們生成實例。但是這很難。我認爲最好的解決方案就是在GHC編譯器中製作票據,讓作者實現衍生別名 :)

+0

您可以勾勒TH解決方案嗎? –

+0

@LeventErkok我寫了TH解決方案的小圖。現在你可以看到爲什麼它不那麼微不足道了:) – Shersh

+0

感謝您的最有幫助的寫作。這工作就像我的用例魅力!非常感激。 –