2012-04-02 65 views
7

其基本思想是我有一系列函數可以處理來自特定類的任何類型的函數,但是在運行時該程序應該讀取配置文件並提取一個元素類中的類型。例如,我有一個'係數'類,它的各種實例,以及各種類型的函數,它們在該類的類型上是多態的;在運行時,該類的一個特定類型將被確定並傳遞。透明地實現一種特定形式的動態輸入


我不確定如何正確解決這個問題;我嘗試編寫'複合'類型,做類似:

data CompoundCoeff = CompoundInt Int | CompoundDouble Double | ... 

其中,Int,Double,...是類「係數」的實例。然而,它開始成爲一個很大的努力來調整代碼中涉及的所有函數來處理這些複合類型(並且它也不是一個很好的解決方案)。如果所有功能都具有相同的簡單類型,例如

Coefficient a => a -> (stuff not involving a anymore) 

但不幸的是並非如此。

另一個問題,我碰到了,是我使用類型的家庭,有一些像

class (Monoid (ColourData c), Coordinate (InputData c)) => ColourScheme c where 
    type ColourData c :: * 
    type InputData c :: * 
    colouriseData  :: c -> (ColourData c) -> AlphaColour Double 
    processInput  :: c -> InputData c -> ColourData c 

這不乾淨,通過走,如果我不得不使用某種化合物ColourData數據類型,如前一個;特別是我不能再保證數據流給出一個一致的類型(而不僅僅是一個複合類型的不同「子類型」),並且如果我組成了一個僞造的Monoid實例,複合ColourData類型。

我也研究過Data.Dynamic,但我再也看不到它將如何正確解決問題;看起來完全相同的問題似乎出現了(甚至稍差,因爲根據我的理解,只有一種「通用」動態類型)。


問:我如何能實現動態數據類型從屬於特定的類,而無需重寫所有涉及這些數據類型的功能?如果我不必犧牲任何類型的安全性,那將是最好的,但我不太樂觀。
該程序應該在運行時讀取配置文件,並且將應用所有必需的函數,這些函數在相關類中是多態的。

回答

7

傳統的方式來提供,保證它是類型類Foo的實例,但不做任何額外的保障對象,就像這樣:

{-# LANGUAGE ExistentialTypes #-} 
data SomeFoo = forall a . Foo a => SomeFoo a 

instance Foo SomeFoo where 
    -- all operations just unwrap the SomeFoo straightforwardly 

,或者與GADTs,這可能是更具可讀性。 ..

data SomeFoo where 
    SomeFoo :: Foo a => a -> SomeFoo 
6

其中一項建議是寫一個頂層函數,它把所有的臨門一腳,一旦你選擇了一個類型:

topLevel :: SomeTypeClass a => a -> IO() 

那麼你的程序可以寫成這樣:

main = do 
    config <- readConfig 
    case config of 
     UseDouble n -> topLevel n 
     UseSymbolic x -> topLevel x 
     UseWidgetFrobnosticator wf -> topLevel wf