假设我正在为F#中的特定于域的语言构建解析器.
我已经定义了一个区分联合来表示表达式:
type Expression =
| Equality of Expression*Expression
| NonEquality of Expression*Expression
| Or of Expression*Expression
| And of Expression*Expression
| If of Expression*Expression
| IfElse of Expression*Expression*Expression
| Bool of bool
| Variable of string
| StringLiteral of string
Run Code Online (Sandbox Code Playgroud)
现在,我已经建立了一个类型的AST,Expression并希望为它生成代码.我有一个函数,它对表达式进行类型推断和类型检查.
它被定义为
let rec InferType expr =
match expr with
| Equality(e1,e2) -> CheckTypes (InferType e1) (InferType e2)
| Or(e1,e2) -> CheckTypes (InferType e1) (InferType e2)
| And(e1,e2) -> CheckTypes (InferType e1) (InferType e2)
...
Run Code Online (Sandbox Code Playgroud)
我还有另一个函数来生成遵循类似模式的代码:获取表达式,为union中的每个项写入模式匹配语句.
我的问题是:这是用F#做的惯用方式吗?
在我看来,如果工会的每个成员都定义了自己的InferType并且GenerateCode在本地定义了它,那将会更加清晰.
如果我使用C#,我会定义一个名为某些抽象基类Expression与虚拟方法InferType和GenerateCode,然后在每个子类覆盖它们.
有没有其他方法可以做到这一点?
Bri*_*ian 18
在我看来,如果工会的每个成员都定义了自己的
InferType并且GenerateCode在本地定义了它,那将会更加清晰.
我相信你的意思是"更熟悉",而不是"更清洁".
真的,您的理想是让代码生成器实现分布在10个不同的类中吗?
在你是否想要"按类型"或"按操作"分组事物之间肯定存在根本的紧张关系.通常的OO方式是"按类型",而FP(函数编程)方式是"按操作".
在编译器/解释器的情况下(或OO中的大多数事情严重依赖于访问者模式),我认为"通过操作"是更自然的分组.用于代码发生器If和And和Or可具有一个共同的位; 各节点的类型测试者同样具有共性; 如果你制作漂亮的打印机,可能会有所有节点漂亮打印实现共同的格式化例程.相比之下,打印,类型检查和代码生成IfElse实际上根本没有多大关系,那么为什么要将这些组合在一个IfElse类中呢?
(回答你的问题:是的,这是惯用的.还有另一种方式 - 是的,你可以像在C#中那样做.我想你会发现你对C#的方式不太满意,而且这样的代码也将大2-3倍,没有任何好处.)
作为OCaml程序员,我认为这完全不是惯用语.顺便提一下,与使用类方法编写类层次结构相比,这可以更好地分离关注点.你会在OO语言中使用InferType访问者获得类似的模块化,但它会有更多的代码.