Haskell implementing different behavior for types to increase code resue

74 views Asked by At

I am updating some code I have. I noticed a lot of repeated code, so I took that as a good sign that some abstraction was needed.

I have several functions that transition a State from one to another. The code to execute the transition is identical for every file.

This:

runOneTest :: Config -> Signal TestResult
runOneTest config = TestResult config <$> result
  where
    result = topEntity startingState inputSignal
    startingState = startS config
    inputSignal   = signal $ input config

What is not identical are the values in the Config Type or the TestResult.

data TestResult = TestResult { initConfig  :: Config
                             , endSt        :: St
                             }deriving (Eq)

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

More specifically the values that are different are the St(State) and the PIn(PortsIn). Those types can have different values attached.

Example:

data PIn = PIn { _clk   :: Bit
               , _reset :: Bool
               , _start :: Bool
               , _stop  :: Bool
               }

vs

data PIn = PIn { _in_1 :: Bit
               , _clk  :: Bit
               , _reset :: Bool
               } 

I was trying to do that with classes but I am running into problems.

Current:

--------------------------------------------------------------------------------
-- Abstract Area
--------------------------------------------------------------------------------
data TestResult = TestResult { initConfig :: (Transition t) => t
                             , endSt      :: (SysState s) => s
                             }

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

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

class Transition t where
  runOneTest :: (Transition t) => t -> Signal TestResult

class Result r

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



--------------------------------------------------------------------------------
-- Stuff to define in each file
--------------------------------------------------------------------------------

runOneTest' :: (Transition t) => t -> Signal TestResult
runOneTest' config = signal $ TestResult config st

data PIn = PIn { _clk   :: Bit
               , _reset :: Bool
               , _start :: Bool
               , _stop  :: Bool
               } deriving (Eq, Show)
instance PortIn PIn

data St = St { _cnt_en   :: Bool
             , _count_us :: BitVector 4
             , _stop_d1  :: Bool
             , _stop_d2  :: Bool
             , _count    :: BitVector 4
             } deriving (Eq, Show)
instance SysState St

st :: St
st = St False 0 False False 0

instance Result TestResult

instance Transition Config where
  runOneTest = runOneTest'

The type of errors I am getting are:

Couldn't match expected type `t1' with actual type `t'
  `t' is a rigid type variable bound by
      the type signature for
        runOneTest' :: Transition t => t -> Signal TestResult
      at ConvertedClashExamples\ClassExample.hs:50:16
  `t1' is a rigid type variable bound by
       a type expected by the context: Transition t1 => t1
       at ConvertedClashExamples\ClassExample.hs:51:31
Relevant bindings include
  config :: t (bound at ConvertedClashExamples\ClassExample.hs:51:13)
  runOneTest' :: t -> Signal TestResult
    (bound at ConvertedClashExamples\ClassExample.hs:51:1)
In the first argument of `TestResult', namely `config'
In the second argument of `($)', namely `TestResult config st'

Original Full Source: https://github.com/LambdaScientist/CLaSH-by-example/tree/master

Note: I looked answers to this question, but I did not find a good enough answer.

0

There are 0 answers