I'm trying to extend the classic GADTs example around eval-ing a Value, but I'm hitting something I don't fully understand.
data Command a where
Base64_ :: Command a
Take_ :: Int -> Command a
-- eval only works with this definition
-- with this uncommented, cmd breaks with:
--
-- Could not deduce: v ~ T.Text
-- from the context: a ~ T.Text
-- bound by a pattern with constructor:
-- VString :: T.Text -> Value T.Text,
-- in an equation for ‘cmd’
-- ‘v’ is a rigid type variable bound by
-- the type signature for:
-- cmd :: forall c a v. Command c -> Value a -> Value v
data Value v where
VString :: T.Text -> Value T.Text
VInt :: Int -> Value Int
VBool :: Bool -> Value Bool
VNot :: Value Bool -> Value Bool
-- cmd only works with this definition
-- with this uncommented, eval breaks with:
--
-- Couldn't match expected type ‘a’ with actual type ‘T.Text’
-- ‘a’ is a rigid type variable bound by
-- the type signature for:
-- eval :: forall a. Value a -> a
-- data Value v where
-- VString :: T.Text -> Value v
-- VInt :: Int -> Value v
-- VBool :: Bool -> Value v
-- VNot :: Value Bool -> Value v
eval :: Value a -> a
eval (VString i) = i
eval (VInt i) = i
eval (VBool b) = b
eval (VNot b) = not $ eval b
cmd :: Command c -> Value a -> Value v
cmd Base64_ (VString s) = VString $ encodeBase64 s
cmd (_) (_) = VInt 3 -- or show error for no match.
Is what I'm trying to do possible? Is there a single GADT definition that would work with both eval and cmd, or am I getting one of the function signatures incorrect?
The type signature:
says that a
Command cof any "type"ctakes aValue aof any "type"aand can produce a valueValue vof any caller-requested "type"v. But, yourBase64_command doesn't take aValue aof any type, it only takes aValue T.Text, and it doesn't produce aValue vof any type the caller wants, it only produces aValue T.Text.What you probably want to do is construct your
Command cGADT so that thecpart determines the permitted inputaand actual outputv.One way of doing this would be to use a GADT with multiple type arguments, like:
where
aindicates the input type, andvindicates the output type for each defined command.Now, the signature:
expresses the idea that a command of a particular type
Command a vfor concrete values ofaandvcan be expected to accept aValue aand produce aValue v, and the following definition will work:Full example: