Home > Software engineering >  Use one Data Constructor into two different Types
Use one Data Constructor into two different Types

Time:12-25

Now I'm trying to write a toy-language-frontend using Haskell.

And there are two entities:

  • rvalue - variables and integer-literals (for example)
  • lvalue - variables only.

I think the most natural way to describe this in data structures is:

data LValue = Var String
data RValue = Var String | Lit Int

(I really think this is the best approach - not verbose and strict enough thanks exhaustive pattern matching) But ghc shows me error: "multiple declarations of Var".

So the question is: what's the most straightforward way in Haskell to describe what I want?

Noteces: There is a similar question 11 years ago with no good answer. But has anything changed for 11 years? One value constructor belonging to two different types

And there is some other approaches with "packing" LValue again (and again and again in less-toy-language) in data-constructor like this:

data LValue = Var String 
data RValue = LValue LValue | Lit Int

But I doubt is this good way: AST-transformation code becomes too verbose.

CodePudding user response:

I actually think your proposal is the best:

newtype LValue = Var String -- newtype is slightly better here, I think
data RValue = LValue LValue | Lit Int

It is almost certainly the case that any time you use an lvalue as an rvalue, you want to treat all lvalues in a uniform way, so you likely won't need or want to do a second level of pattern matching at that moment; in other words, I claim there will be less verbosity than you might expect at first.

There are basically two alternatives. The first is to make a class:

newtype LValue = LVar String
data RValue = RVar String | Lit Int

class HasVariables ty where var :: String -> ty
instance HasVariables LValue where var = LVar
instance HasVariables RValue where var = RVar

This way you can use var for constructing either LValues or RValues. You will still need to distinguish between them syntactically when pattern-matching though.

The second is to make a GADT:

data Side = L | R
data Value side where
    Var :: String -> Value side
    Lit :: String -> Value R
  • Related