在Haskell中,如何将类型类与该类型类的实例进行匹配?

时间:2017-01-01 02:25:23

标签: 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'

我认为我可以使用输入',因为它会在pin的实例中产生PIn。

我在某处有误会,希望有人可以解释我所缺少的东西。

2 个答案:

答案 0 :(得分: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'

答案 1 :(得分: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类型中pininput所喜欢的任何类型,只要cConfiguration的实例,pin就是PortInShow的实例。 (这是所有多态函数的工作方式:even :: Integral a => a -> Bool表示调用者可以将even应用于Integral实例的任何类型的值,fromInteger :: Num a => Integer -> a表示调用者可以将fromInteger的结果视为具有Num实例的任何类型。)

因此,input的{​​{1}}方法的实现者必须提供适用于任何类型Configuration Config的实现pinPortIn的实例。但您的实施Show只能适用于单一类型input = input'。这就是编译器拒绝它的原因。 (它告诉你结果的PIn类型是" rigit类型变量",这意味着你不能选择它作为实现者,但必须假设它已经&# 34;严格地"被来电者选中。)

关于如何处理这个问题,你在Gurkenglas的codereview问题评论中得到了一个很好的答案:你可以根据端口和状态的类型简单地参数化pin和其他类型。