Exploring F# through modeling #2

Introduction

In the last post we created a simple rock-paper-scissor game. There are still a lot of improvements to be made to the Game itself, we’re going to concentrate on doing some simple command handling in this post. As for the technical part, we’re going to learn how to use pipes “|>”. (forward)

Handling

In order to handle a command, we need to;

  • Read all earlier events related to the game instance for the command.
  • Replay all these events on the game
  • Apply the command on the game
  • Save the events caused by the command

To do this we need a store for the events, an eventstore. In our code we’re going to fake the event store’s load and save methods, to later revisit and implement/use something real.

Piping

This flow could be represented in a readable way using “|>” pipe. This takes the result from the left side and adds it as a paramter to the right. Our handle flow could be a function (called persist);

Here we load, rehydrate and then handle the command via a function that we pass (more on that later) and finally saves. Notice that we pass around our store to load and save functions.

Load

To read all earlier events we create a load funcation that takes a store.

Our store is implemented as a dictionary to keep it simple. So the load function looks like;

Replay

In the last post we created a function to restore the game state, a function called restoreState, that takes an initial state, events and return the current state.

In our handler we use this in a function called rehydrate.

Apply

Our persist function takes another function for handling the command, in the above code called f. It needs to return events to pass to the save function.

The store is a dictionary.

With the store (dictionary) in place we create a function to help us using it together with the function persist. A function called apply;

Apply is an partial application.

Now we need something that takes commands and uses the apply function, and specifies how the commands should be handled.

Here a lot of interesting stuff is going on. A function called handle takes any object as paramter. We then use our old friend match now togheter with “:?” to check the type of the command. If the command matches we create an alieas and use the apply function. The apply function uses our flow (persist function) passed the id and a function telling it how the command should be used on the rehydrated game. MakeMoveCommand is going to invoke makeMove on the Game that rehydrate returns with the command (c). Also note that the apply function is using persist and passing c.id and a function to handle the command.

Save

Due to the simple nature of this implementation we let the save function be empty. Load and save will get more interesting the day we choose to use a real eventstore. In the example below, we could add the events to the dictonary.

End

Let se how it all look put together. (Note the we changed the id’s from guid to strings since last post).

Lessons learned

  • We learned how to use “|>” pipe to make a readable flow.
  • We learned how to use “:?” to match with types.
  • We made the apply to ease the use of the persist function, the apply function then takes the remaining parameters and passes the to persist.
  • We learned how to pass a function (persist parameter f)
  • We learned how pass the function and execute commands against Game (in the handle method)
  • We introduced apply as a partial application. Also check out currying and the difference.

Conclusion

We continued with creating the handler instead of improving the Game for the last post. The goal is to get working parts before revisiting them to learn more. As one reader pointed out on the comments of the last post, commands and events could be made as a discriminated union. Maybe something for future refactorings.

Enjoy!

This Post Has One Comment

  1. It would seem more natural to me to have the application layer export the “step” function from your last post, rather than the full restoreState, and use the foldBack function directly in the rehydrate function, so that the “framework” could decide to restore state from a snapshot if available, correct?. Or is that for the next refactoring?

Leave a Reply

Close Menu