2017-01-01 105 views
4

我已經定義了以下類:在Haskell中,我如何將一個類型類與該類型類的一個實例進行匹配?

class Configuration c where 
    input :: (PortIn pin, Show pin) => c -> pin 
    startSt :: (SysState st, Show st) => c -> st 

class (Eq pin, Show pin) => PortIn pin 

class (Eq st, Show st) => SysState st 

以下類型和實例:

--inputs 
data PIn = PIn { _clk :: Bit 
       , _reset :: Bool 
       , _start :: Bool 
       , _stop :: Bool 
       } deriving (Eq) 
instance PortIn PIn 
instance Show PIn where 
    show PIn {..} = 
    "PIn\n\t _clk = " P.++ show _clk 
    P.++ "\n\t _reset = " P.++ show _reset 
    P.++ "\n\t _start = " P.++ show _start 
    P.++ "\n\t _stop = " P.++ show _stop 

--Outputs and state data 
data St = St { _cnt_en :: Bool 
      , _count_us :: BitVector 4 
      , _stop_d1 :: Bool 
      , _stop_d2 :: Bool 
      , _count :: BitVector 4 
      } deriving (Eq) 

instance SysState St 
instance Show St where 
show St {..} = 
     "St\n\t _cnt_en = " P.++ show _cnt_en 

    P.++ "\n\t _count_us = " P.++ show _count_us 
    P.++ "\n\t _stop_d1 = " P.++ show _stop_d1 
    P.++ "\n\t _stop_d2 = " P.++ show _stop_d2 
    P.++ "\n\t _count = " P.++ show _count 

爲什麼我無法在這裏創建配置的實例:

data Config = Config { input' :: PIn 
        , startSt' :: St 
        } deriving (Eq) 

instance Show Config where 
show Config {..} = 
     "Config:\n input = " P.++ show input' 
    P.++ "\n startSt = " P.++ show startSt' 

instance Configuration Config where 
    input = input' 
    startSt = startSt' 

我得到以下錯誤:

Couldn't match type ‘pin’ with ‘PIn’ 
    ‘pin’ is a rigid type variable bound by 
     the type signature for 
      input :: (PortIn pin, Show pin) => Config -> pin 
     at Clks_n_regs_4.hs:101:3 
Expected type: Config -> pin 
    Actual type: Config -> PIn 
Relevant bindings include 
    input :: Config -> pin (bound at Clks_n_regs_4.hs:101:3) 
In the expression: input' 
In an equation for ‘input’: input = input' 

我想我可以用輸入」,因爲它會導致引腳,引腳中的一個實例。

我有一個誤解的地方,希望有人可以解釋我失蹤。

+5

這看起來很像你試圖將OO類擠入Haskell。你爲什麼需要這個(或_think_你需要它)? – leftaroundabout

+0

我有幾種版本的類型:Config,PIn,St和一個名爲'runOneTest'的函數。使用類型和函數的代碼是相同的。 'runOneTest'的類型簽名將始終是'runOneTest :: Config - > Signal TestResult'我希望能夠重新使用調用'runOneTest'函數的所有其他代碼。我昨天在CodeReview上發佈了這個消息,但沒有任何結果。 [http://codereview.stackexchange.com/questions/151278/abstracting-haskell-types-to-increase-code-reuse](Code) – LambdaScientist

回答

4

要了解爲什麼您的代碼不輸入檢查,請參閱:existential vs. universally quantified types。要做到這一點,一種可能性是使用Functional Dependencies。然後你的類型級將是:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-} 

class (PortIn pin, Show pin, SysState st, Show st) => Configuration c pin st 
    | c -> pin, c -> st where 
    input :: c -> pin 
    startSt :: c -> st 

和實例的定義是:

instance Configuration Config PIn St where 
    input = input' 
    startSt = startSt' 

這會讓你的代碼類型檢查,雖然可能不是你所追求的。

不同的方向將使用existential types。在這種情況下:

{-# LANGUAGE ExistentialQuantification #-} 

data PortInShow = forall a . (PortIn a, Show a) => PortInShow a 
data SysStateShow = forall a . (SysState a, Show a) => SysStateShow a 

class Configuration c where 
    input :: c -> PortInShow 
    startSt :: c -> SysStateShow 

instance Configuration Config where 
    input = PortInShow . input' 
    startSt = SysStateShow . startSt' 
4

鏈接的問題在behzad.nouri的回答適用性可能不是很清楚的人誰不已經明白這是怎麼回事的。爲了更詳細地回答實際問題:

class Configuration c where 
    input :: (PortIn pin, Show pin) => c -> pin 
    startSt :: (SysState st, Show st) => c -> st 

意味着input的類型將是

input :: (Configuration c, PortIn pin, Show pin) => c -> pin 

這反過來又意味着呼叫者的input可以選擇他們喜歡c任何類型和pin中的input的類型,只要cConfiguration的實例,pinPortIn和的實例。 (這是所有多態函數的工作方式:even :: Integral a => a -> Bool意味着調用者可以將even應用於作爲Integral實例的任何類型的值,並且fromInteger :: Num a => Integer -> a意味着調用者可以將fromInteger的結果視爲具有任何類型,該類型是Num的實例。)

由此可見你,爲Configuration Configinput方法的實施者,必須提供對任何類型pin那就是PortInShow實例工程的實現。但是您的實施input = input'只可能適用於單一類型PIn。這就是編譯器拒絕它的原因。 (它告訴你結果的類型pin是一個「嚴格類型變量」,這意味着你不能選擇它作爲執行者,但必須假定它已經被調用者「嚴格地」選擇了。)

至於怎麼做,你已經在Gurkenglas的codereview問題的評論中得到了很好的回答:你可以根據需要簡單地參數化Config和其他類型的端口和狀態。

相關問題