TIC was originally written in C#. At some moment I was interested in a better way to write the AI, and that started me on learning F#. Quickly I came to the conclusion, that it would be best to rewrite the TIC-webservice in F#. This webservice handles the full game, while uncoupled GUI’s may connect, to display the game’s current state, or to invoke a player’s move.
To reduce this problem into its (al)most simplest form:
So how would we model this in F# code?
Let’s get deeper into the problem first.
Copy/paste the following into an F# script file and run it in the F# Interactive:
| Instance of string * LinkObject
| Empty
let a = ("A", Empty)
let b = ("B", a)
let c = ("C", b)
val b : string * (string * LinkObject) = ("B", ("A", Empty))
val c : string * (string * (string * LinkObject)) = ("C", ("B", ("A", Empty)))
("A", ("C", ("B", ("A", Empty))))
If we did the same thing in C#, and “a” was a mutable variable instead of an immutable value, then we would have been finished now. We can also make it mutable in F#, however we would need this mutability only at initialization. After initialization, we don’t want it mutable anymore. But if we make it mutable, it will be mutable forever and we then open the door for side-effects.
So if we go back to the problem that immutability gives us, what is the problem exactly? It is a chicken-egg problem:
- When we first create “A”, we cannot link to “C”, because it does not exist;
- After creating “C”, we cannot change the reference in “A”, because its immutable;
Now let’s make a bold statement. If we create “A”, we cannot link it to “C”, because it does not exist. However: we do know that we want to link to “C”, when we create “A”. We already have the idea in our head. The moment that the link was laid - when we got the idea - is in the past, we are just trying to realize it in the machine.
So if we see the creation of objects, and linking them together as separate concerns, then we have solved this problem. In the immutable world, you cannot link directly, if your data allows cyclic graphs. So linking becomes a step-in-between.
Try this example in the F# Interactive:
In this example we use an integer to identify a data object, which acts as a lookup key. Of course this scheme can be simplified, or enriched, according to your requirements.
Although the Hexagon board is a little more complex, in essence it is based upon the above principle.