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 LValue
s or RValue
s. 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