2

I want to compile a syntax tree to methods of aTurtle module.

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

This option produces code duplication (There are actually more commands):

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

I replaced it with this:

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

Now I have a command Turtle.moveAndEvadeCollision. It depends on other commands. Particularly, it should be executed after each move command, to ensure it doesn't move into occupied position. Also, it shouldn't be followed by a move command in a tree.

I cannot write a method, which validates, that moveAndEvadeCollision is not followed by move because they are all undistinguishable TurtleState -> TurtleState lambdas. Does it mean that my first refactoring was wrong and I should return to duplication? Is it normal for a program in a functional language, to have a data structure consisting of functions?

user2136963
  • 2,526
  • 3
  • 22
  • 41

1 Answers1

2

I think the first version is better, because it separates the (input -> command) and (command -> movement) phases. That is a very common design for interpreters, corresponding to the parsing and execution stages respectively.

I don't think there's any real code duplication, though the code does end up being a bit longer. But you get some very real benefits, such as being able to have the compiler validate that your applyState function handles all possible commands. You can also test each part independently.

Furthermore, as you have already discovered, if you parse the input into a tree of commands first you can do some static verification of the whole tree to make sure that you haven't entered any invalid programs.

Martin DeMello
  • 11,876
  • 7
  • 49
  • 64