代码之家  ›  专栏  ›  技术社区  ›  HS.

F设计模式

f#
  •  8
  • HS.  · 技术社区  · 16 年前

    假设我正在用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 想为它生成代码。 我有一个函数对表达式进行类型推断和类型检查。

    它的定义是

        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_的惯用方法吗?

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

    如果我使用c,我将定义一些抽象基类 表情 使用虚拟方法 推断式 生成代码 然后在每个子类中重写它们。

    还有别的办法吗?

    3 回复  |  直到 16 年前
        1
  •  17
  •   Brian    16 年前

    在我看来 如果工会的每个成员 自己定义的 InferType GenerateCode 在本地使用它。

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

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

    无论你是想“按类型”还是“按操作”对事物进行分组,两者之间肯定存在着一种基本的紧张关系。通常的oo方式是“按类型”,而fp(函数式编程)方式是“按操作”。

    在编译器/解释器的情况下(或者在oo中,大多数东西严重依赖于访问者模式),我认为“按操作”是更自然的分组。的代码生成器 If And Or 可能有一些共同点;各个节点的typechecker也有类似的共同点;如果您制作了漂亮的打印机,那么可能会有所有漂亮的节点打印实现所共有的格式化例程。相比之下,打印、排版和编码 IfElse 真的一点关系都没有,所以你为什么要把那些 IFIFER 班级?

    (回答你的问题:是的,这是惯用语。有没有别的方法-是的,你可以像在C_里那样做。我想你会发现你对C方式不太满意,而且代码也会比C方式大2-3倍,没有任何好处。)

        2
  •  9
  •   Tobu    16 年前

    作为一个ocaml程序员,我认为这完全是一个习惯用法。顺便说一下,这比用类方法编写类层次结构更好地分离关注点。在一个带有infertype访问器的oo语言中,您会得到类似的模块化,但这将是更多的代码。

        3
  •  1
  •   kvb    16 年前

    在函数式语言中,通常可以做的另一件事是定义 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 ...