将一个数据构造函数用于两种不同的类型

Use one Data Constructor into two different Types

现在我正在尝试使用 Haskell 编写一个玩具语言前端。

并且有两个实体:

我认为在数据结构中最自然的描述方式是:

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

(我真的认为这是最好的方法——不冗长且不够严格,感谢详尽的模式匹配) 但是 ghc 向我显示错误:“多个 Var 声明”。

所以问题是:Haskell 中描述我想要的内容的最直接方式是什么?

注意事项: 11年前有一个类似的问题没有好的答案。但是11年有什么改变吗? One value constructor belonging to two different types

还有一些其他方法在数据构造函数中再次“打包”LValue(并一次又一次地使用非玩具语言),如下所示:

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

但我怀疑这是不是一个好方法:AST 转换代码变得过于冗长。

其实我觉得你的提议是最好的:

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

几乎可以肯定的是,无论何时将左值用作右值,您都希望以统一的方式处理所有左值,因此您可能不需要或不想进行第二级模式匹配那一刻;换句话说,我声称会比您最初预期的要少冗长。

基本上有两种选择。首先是制作一个 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

这样您就可以使用 var 来构建 LValueRValue。不过,在进行模式匹配时,您仍然需要在语法上区分它们。

二是制作GADT:

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