Ji, there!
I want to make a list that contains tuples, such tuples consist of a string and the second element an integer or boolean
I found this answer: Tuples with different types
But I think it's a bit different, because I have to work with my data already defined, not with Int or Bool, the problem is that I can't put Num (by Int) or B (by Bool) back.
My code
data EAB = Var String
| Num Int
| B Bool
| Sum EAB EAB
| Prod EAB EAB
| Neg EAB
| Pred EAB
| Suc EAB
| And EAB EAB
| Or EAB EAB
| Not EAB
| Iszero EAB
| If EAB EAB EAB
| Let EAB EAB
| Abs String EAB
-- Arithmetic and Boolean expression language
-- Example that I want
-- Ctx = [("x",Num),("y", B),("z", B),("w",Num)]
-- I tried
-- data Type = Num EAB | B EAB
-- type Ctx = [(String, Type)]
-- but doesn't work
This is because I want to define a function that tells me if I can do the evaluation of equal types, based on my EAB, for example
vt :: Ctx → EAB → Type → Bool
--- ...
-- Results
vt [] 4 5 Num = True
vt [] 4 False Num = False
vt [(x,Num)] 4 x Num = True
Thanks!
CodePudding user response:
Simply make a fresh type, with fresh constructors.
data Type = NumT | BT
ctx = [("x", NumT), ("y", BT), ("z", BT), ("w", NumT)]
vt :: Ctx -> EAB -> Type -> Bool
vt [] (Sum (Num 4) (Num 5)) NumT = True -- etc.
CodePudding user response:
You can't make the list you want, but even if you could you can't make your vt
function either.
You seem to be intending to use the B
and Num
constructors of your EAB
type to represent a type in your EAB language. That isn't going to be possible.
Constructors are special in Haskell because you can use them in pattern matching. You can write a pattern where you fully apply a constructor to other patterns, and then check whether that matches a value of the pattern's type. Like so:
case expr :: EAB
of Var s -> "Var: " <> s
Num n -> "Num: " <> show n
B b -> "B: " <> show b
Outside of that context constructors are nothing special; they're just functions1. So your Num
is a function of type Int -> EAB
, and your B
is a function of type Bool -> EAB
. You cannot compare functions, in any way. The only thing you can do with a function is apply it (to a correctly-typed argument) and see what you get.
This means that your imagined vt
is impossible. In that third argument (which you said should be of a type Type
that you haven't defined), you've used Num
. But remember, Num
is just a function of type Int -> EAB
. There's no way for the vt
function implementation to look at the function it receives and test whether it is Num
in order to help decide whether the return value should be True
or False. The only thing it could possibly do is apply it to an
Intand then inspect the
EAB` value.
But you presumeably also want to be able to pass B
in that position, so that vt
can say whether a given EAB
expression can be evaluated to a boolean value. B
is a function with a different type Bool -> EAB
. The only way for the third parameter of vt
to accept either type of function is to generalise it with a type variable, making this parameter be (a -> EAB)
. But then there is no way for vt
to know what this function could be applied to, so it won't even be able to do the one thing you can do with functions!2
Basically Num
and B
cannot be used as abstract representations of the types in your language. They are constructors in the EAB
datatype that help you build and inspect values in your language. That is all they are for. Because of that there's really no point going further into why you can't mix Num
and B
in (tuples inside) a list3 or how you might be able to work around it. What you're trying
What Daniel Wagner suggested is simply to stop trying to abuse the constructors of EAB
for this purpose and just make a datatype to represent types in your language! This is much better anyway because EAB
has 15 constructors but your language appears to only have two types.4
So you simply do this:
data EABType = NumT | BT
deriving (Eq, Show)
type Ctx = [(String, EABType)]
-- e.g. [("x",NumT),("y", BT),("z", BT),("w",NumT)
vt :: Ctx -> EAB -> EABType -> Bool
vt ctx expr ty
= case expr of
-- making some guesses about how you want vt to behave
Var s -> (s, ty) `elem` ctx
B _ -> ty == BT
Num _ -> ty == NumT
Sum l r -> ty == NumT && vt ctx l BT && vt ctx r BT
And l r -> ty == BT && vt ctx l BT && vt ctx r BT
...
1 Unless the constructor has no arguments, then it's not a function. You still can't compare them except by pattern matching (an Eq
instance is trivially implemented with pattern matching). But a nullary constructor on its own is a complete pattern so there's no such thing as an unapplied nullary constructor that you can't compare.
2 Hypothetically we could use a type class constraint on the variable, which might make it possible to call the function. e.g. If it was (Monoid a => a -> EAB)
, then we can always call it with mempty
. There isn't anything you can do with this idea that will actually help you use this parameter for the purpose you intended though, so I won't go further in this direction.
3 But to hammer the point home: it's the same reason as before. B
and Num
in the context you've used them are simply functions with different types, you cannot mix differently typed functions in a list, and even if you could you wouldn't be able to do anything with them once they were in the list (like look at each tuple in the list and check which function was there).
4 Assuming you're going for simple value types, and not trying to assign types like Bool -> Bool -> Bool
to to unapplied functions.