F#设计模式

HS.*_*HS. 8 f#

假设我正在为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与虚拟方法InferTypeGenerateCode,然后在每个子类覆盖它们.

有没有其他方法可以做到这一点?

Bri*_*ian 18

在我看来,如果工会的每个成员都定义了自己的InferType并且 GenerateCode在本地定义了它,那将会更加清晰.

我相信你的意思是"更熟悉",而不是"更清洁".

真的,您的理想是让代码生成器实现分布在10个不同的类中吗?

在你是否想要"按类型"或"按操作"分组事物之间肯定存在根本的紧张关系.通常的OO方式是"按类型",而FP(函数编程)方式是"按操作".

在编译器/解释器的情况下(或OO中的大多数事情严重依赖于访问者模式),我认为"通过操作"是更自然的分组.用于代码发生器IfAndOr可具有一个共同的位; 各节点的类型测试者同样具有共性; 如果你制作漂亮的打印机,可能会有所有节点漂亮打印实现共同的格式化例程.相比之下,打印,类型检查和代码生成IfElse实际上根本没有多大关系,那么为什么要将这些组合在一个IfElse类中呢?

(回答你的问题:是的,这是惯用的.还有另一种方式 - 是的,你可以像在C#中那样做.我想你会发现你对C#的方式不太满意,而且这样的代码也将大2-3倍,没有任何好处.)


Tob*_*obu 9

作为OCaml程序员,我认为这完全不是惯用语.顺便提一下,与使用类方法编写类层次结构相比,这可以更好地分离关注点.你会在OO语言中使用InferType访问者获得类似的模块化,但它会有更多的代码.