Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Command Pattern

I am trying to implement the command pattern to control a robot. I'm using this to explore how to implement the command pattern in F#. Below is my implementation:

type Walle(position, rotate) =     
    let (x:float,y:float) = position
    let rotation = rotate
    member this.Move(distance) =
        let x2 = distance * sin (System.Math.PI/180.0 * rotation)
        let y2 = distance * cos (System.Math.PI/180.0 * rotation)
        let newPosition = (x+x2, y+y2)
        Walle(newPosition, rotation)
    member this.Rotate(angle) = 
        let newRotation = 
            let nr = rotation + angle
            match nr with
            | n when n < 360.0 -> nr
            | _ -> nr - 360.0
        Walle(position, newRotation)

let Move distance = fun (w:Walle) -> w.Move(distance)
let Rotate degrees = fun (w:Walle) -> w.Rotate(degrees)

let remoteControl (commands:List<Walle->Walle>) robot = 
    commands |> List.fold(fun w c -> c w)

let testRobot() =
    let commands = [Move(10.0);Rotate(90.0);Move(16.0);Rotate(90.0);Move(5.0)]
    let init = Walle((0.0,0.0),0.0)
    remoteControl commands init

In an effort to come up with a functional solution I chose to make the robot's actions return a new instance of the robot at its new position after each invocation (avoid mutation). I also made the command functions close over the state required to execute the actions.

I was curious whether people feel these were good design decisions when implementing the pattern? Or, if there was any other advice people could give on implementing the pattern?

like image 400
David Dickson Avatar asked Dec 12 '22 13:12

David Dickson


2 Answers

To avoid going the OO way of combining data with operations in a "type" and representing this combination as "Object", a more functional approach in my POV would be to define data and operations separately in a module as shown below:

module Walle = 
 type Walle = {Position : float * float; Rotation : float}

 let Move distance (w:Walle) = 
    let x2 = distance * sin (System.Math.PI/180.0 * w.Rotation)        
    let y2 = distance * cos (System.Math.PI/180.0 * w.Rotation)
    {w with Position = (w.Position |> fst) + x2, (w.Position |> snd) + y2 }

 let Rotate angle (w:Walle) = 
    let newRotation = 
        let nr = w.Rotation + angle
        match nr with
        | n when n < 360.0 -> nr
        | _ -> nr - 360.0
    {w with Rotation = newRotation}

Now you can create a new Walle and use |> function to pass that to a series of functions that transform the Walle "data". It is all about data and transformation on that data, no objects :). It may not feel like a command pattern as that is more suited for OO style. You really don't need patterns in FP or do we ?

like image 80
Ankur Avatar answered Dec 25 '22 02:12

Ankur


For the robot example, I would rather just use the imperative style, i.e. changing the states of the robot object. Because a robot object usually has the notion of states and actions to change the states. From the OO design perspective, some kinds of objects are better to be immutable, e.g., String, DateTime in .NET, but a lot of them are not.

Immutable objects of course have advantages. In the persistent version in your question, you can save all the past states of the robot and can easily UnDo commands on the robot.

like image 27
Yin Zhu Avatar answered Dec 25 '22 03:12

Yin Zhu