lambda 树与可区别联合树

Tree of lambdas vs tree of discriminated unions

我想将语法树编译成Turtle模块的方法。

module Turtle =
    let rotateDefaultAmount amount state = ...
    let move vector state = ...

这个选项产生代码重复(其实还有更多的命令):

type TurtleCommand =
    | Rotate of float32
    | Move of Vector2
/...
match symbol with
        | 'w' -> Move (Vector2.up)
        | '\' -> Rotate 90.0f
//...
let applyCommand command state =
    match command with
            | Move shift -> Turtle.move shift state
            | Rotate amount -> Turtle.rotate amount state
applyCommand command someState

我用这个替换了它:

type TurtleCommand = TurtleState -> TurtleState
/...
match symbol with
        | 'w' -> Turtle.move (Vector2.up)
        | '\' -> Turtle.rotate 90.0f
//...
command someState

现在我有一个命令Turtle.moveAndEvadeCollision。这取决于其他命令。特别是,在每一个move命令之后,它应该被执行,以确保它不会移动到被占用的位置。此外,它不应该跟在树中的 move 命令。

我无法编写一个方法来验证 moveAndEvadeCollision 后面没有跟 move 因为它们都是无法区分的 TurtleState -> TurtleState lambda。这是否意味着我的第一次重构是错误的,我应该 return 重复?对于函数式语言的程序来说,具有由函数组成的数据结构是否正常?

我认为第一个版本更好,因为它将(输入 -> 命令)和(命令 -> 移动)阶段分开了。那是解释器很常见的设计,分别对应解析和执行阶段。

我不认为有任何真正的代码重复,尽管代码最终确实有点长。但是您会得到一些非常实际的好处,例如能够让编译器验证您的 applyState 函数是否处理了所有可能的命令。您也可以独立测试每个部分。

此外,正如您已经发现的那样,如果您首先将输入解析为命令树,则可以对整个树进行一些静态验证,以确保您没有输入任何无效程序。