Haskell 断言一个类型将匹配另一个

Haskell assert that a type will match another

我需要让 GHC 知道在类型内部使用的值将与函数输入相同。

配置定义为:

data Config = forall p s . (PortIn p, SysState s, Show p, Show s) =>
              Config { input   :: p
                     , startSt :: s
                     }

它的class是:

class Show t => Transition t where
  runOneTest :: forall st pin . (SysState st, PortIn pin)
             => t
             -> (st -> Signal pin -> Signal st)
             -> Signal TestResult

实例为:

instance Transition Config where
  runOneTest = runOneTest'

runOneTest' :: forall st pin . (SysState st, PortIn pin)
            => Config
            -> (st -> Signal pin -> Signal st)
            -> Signal TestResult
runOneTest' config@Config{..} topEntity' = TestResult config <$> result
  where
    result = topEntity' startingState inputSignal
    startingState = startSt
    inputSignal   = signal input

我遇到错误:

Couldn't match expected type `st' with actual type `s'
`s' is a rigid type variable bound by
      a pattern with constructor
        Config :: forall p s.
                  (PortIn p, SysState s, Show p, Show s) =>
                  p -> s -> Config,
      in an equation for runOneTest'
      at ConvertedClashExamples\TestProc.hs:61:20   
`st' is a rigid type variable bound by
       the type signature for
         runOneTest' :: (SysState st, PortIn pin) =>
                        Config -> (st -> Signal pin -> Signal st) -> Signal TestResult
       at ConvertedClashExamples\TestProc.hs:57:23 
Relevant bindings include
  result :: Signal st (bound at ConvertedClashExamples\TestProc.hs:63:5)
  startingState :: s (bound at ConvertedClashExamples\TestProc.hs:64:5)
  topEntity' :: st -> Signal pin -> Signal st (bound at ConvertedClashExamples\TestProc.hs:61:31)
  startSt :: s (bound at ConvertedClashExamples\TestProc.hs:61:20)
  runOneTest' :: Config
                 -> (st -> Signal pin -> Signal st) -> Signal TestResult
    (bound at ConvertedClashExamples\TestProc.hs:61:1)
In the first argument of topEntity', namely `startingState'
In the expression: topEntity' startingState inputSignal

我认为问题是: GHC 无法知道 startSt 和输入将与我将传入的 topEntity 函数兼容。它只知道它们使用一些相同的 classes。

您的分析是正确的:调用者可以传递一个 Config 值,该值包含 topEntity' 所需的不同类型。

一种选择是避免存在 Config 类型并将其转换为显式

data Config p s = ...

runOneTest' :: forall st pin . (SysState st, PortIn pin)
        => Config pin st
        -> (st -> Signal pin -> Signal st)
        -> Signal TestResult
...

另一种选择是使用 Data.Typeable 执行运行时类型检查。类似于:

import Data.Typeable

data Config = forall p s . (PortIn p, SysState s, Show p, Show s, Typeable s) =>
          Config { input   :: p
                 , startSt :: s
                 }

runOneTest' :: forall st pin . (SysState st, PortIn pin)
        => Config
        -> (st -> Signal pin -> Signal st)
        -> Signal TestResult
runOneTest' config@Config{..} topEntity' = TestResult config <$> result
  where
    result = topEntity' startingState inputSignal
    startingState = startSt
    inputSignal   = signal input2
    input2        = case cast input :: st of
                       Just i  -> i
                       Nothing -> error "wrong runtime type!"