Home > Back-end >  Lists of different tuples in haskell
Lists of different tuples in haskell

Time:04-07

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 theEAB` 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.

  • Related