These are simple representations of sample Input and Output types:
Input = I1 Int | I2 String
Output = OA String | OB Bool | OC
Parameters here are just for the sake of greater realism. :)
I would like to get a function that maps Input
to Output
:
inputToOutput = \case
I1 val -> OA (show val)
I2 str -> (OB (str == "x"))
But this function should be typed checked against allows maps (I1 -> OB
,I2 -> OB
), so I could NOT do:
inputToOutput = \case
I1 val -> OB True -- allows only OA
I2 str -> OA str -- allows only OB
I have a solution variant with GADTs and Families, here I have to introduce additional tags for each of Input
and Output
values:
-- Input tags
data I_ = I1_ | I2_
data Input (i :: I_) where
I1 :: Int -> Input 'I1_
I2 :: String -> Input 'I2_
-- Output tags
data O_ = OA_ | OB_ | OC_
data Output (o :: O_) where
OA :: String -> Output 'OA_
OB :: Bool -> Output 'OB_
OC :: Output 'OC_
-- Input/Output rules for tags
type family I2O (i :: I_) :: O_ where
I2O 'I1_ = 'OA_
I2O 'I2_ = 'OB_
inputToOutput :: Input a -> Output (I2O a)
inputToOutput = \case
I1 val -> OA (show val)
I2 str -> (OB (str == "x"))
My first question: is this the only way to achieve what I want (Using GADTs/Families)? what I don't like there, that I need to introduce additional boilerplate tags for
Input/Output
values, is it possible to make it less boilerplaty way?My second question: is a more advanced logic of mapping possible? Say I want the input to be mapped to a list of outputs that is required to contain one/serveral of the values:
inputToOutput = \case
I1 val -> [ OA (show val), OC] -- also allows other Output values like OC, but not requires
I2 str -> [(OB (str == "x"))]