LambdaScientist
LambdaScientist

Reputation: 435

Haskell assert that a type will match another

I need to let GHC know that the value being use inside of a type will be the same as a functions inputs.

Config is defined as :

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

Its class is:

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

The instance is:

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

I am getting the errors:

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

What I think the problem is: GHC has no way of knowing that the startSt and input are going to be compatible with the topEntity function I will be passing in. It just knows that they use some of the same classes.

Upvotes: 3

Views: 502

Answers (1)

chi
chi

Reputation: 116139

Your analysis is right on the spot: the caller can pass a Config value which carries different types of those required by topEntity'.

One option is to avoid the existential Config type and turn it into an explicit one

data Config p s = ...

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

Another option is to perform a runtime type check using Data.Typeable. Something like:

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!"

Upvotes: 3

Related Questions