【问题标题】:F# design patternF# 设计模式
【发布时间】:2010-12-28 12:46:50
【问题描述】:

假设我正在为 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

现在,我已经建立了一个 Expression 类型的 AST,并希望为它生成代码。 我有一个函数可以对表达式进行类型推断和类型检查。

定义如下

    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)
        ...

我还有另一个函数来生成遵循类似模式的代码:获取一个表达式,为联合中的每个项目编写模式匹配语句。

我的问题是:这是在 F# 中的惯用方式吗?

在我看来,如果工会的每个成员都用它在本地定义自己的InferTypeGenerateCode,那会更干净。

如果我使用 C#,我将定义一些名为 Expression 的抽象基类,并为 InferTypeGenerateCode 定义虚拟方法,然后在每个子类中覆盖它们。

还有其他方法吗?

【问题讨论】:

    标签: f#


    【解决方案1】:

    在我看来,它会是 如果工会的每个成员都更清洁 定义了自己的InferTypeGenerateCode 本地用它。

    我相信你的意思是“更熟悉”,而不是“更干净”。

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

    在“按类型”或“按操作”对事物进行分组之间肯定存在根本性的矛盾。通常的 OO 方式是“按类型”,而 FP(函数式编程)方式是“按操作”。

    对于编译器/解释器(或者 OO 中的大多数东西严重依赖访问者模式),我认为“按操作”是更自然的分组。 IfAndOr 的代码生成器可能有一点共同点;各个节点的类型检查器将同样具有共性;如果你制作了一个漂亮的打印机,那么可能会有所有节点漂亮打印实现通用的格式化例程。相比之下,IfElse 的打印、类型检查和代码生成确实彼此之间没有太大关系,那么为什么要将它们分组到 IfElse 类中呢?

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

    【讨论】:

    • 谢谢 - 这是我正在寻找的答案。
    【解决方案2】:

    作为一名 OCaml 程序员,我会说这完全是惯用的。顺便说一句,与使用类方法编写类层次结构相比,这为您提供了更好的关注点分离。您会在带有 InferType 访问者的 OO 语言中获得类似的模块化,但代码量会更多。

    【讨论】:

      【解决方案3】:

      您通常在函数式语言中可能会做的另一件事是在数据类型上定义fold 操作,然后根据折叠定义类型检查和代码生成函数。在这种特殊情况下,我不确定它是否对您有好处,因为 fold 函数会包含太多参数,因此不太容易理解:

      let rec fold eqE nonE andE orE ... = function
      | Equality(e1,e2) -> eqE (e1 |> fold eqE nonE ...) (e2 |> fold eqE nonE ...)
      | NonEquality(e1,e2) -> nonE ...
      ...
      
      let inferTypes = fold checkTypes checkTypes checkTypes ...
      

      【讨论】:

      • fold 是一种更丑陋的建立访客的方式吗?我还是更喜欢 OP 的风格,inferTypes 直接由模式匹配语句定义。
      • @Tobu:我个人觉得它没有访客模式那么难看。正如我所说,在这个特殊的例子中,有很多不同的情况,我不认为用折叠来定义事物会更好。不过,一般来说,在递归数据类型(例如 F# 的内置 List.fold)上定义折叠操作通常是有意义的。
      猜你喜欢
      • 1970-01-01
      • 2010-10-18
      • 1970-01-01
      • 2023-03-18
      • 2011-10-20
      • 2013-08-27
      • 2012-04-14
      相关资源
      最近更新 更多