I have this record type.
type cell = { alive : bool ; column : int ; row : int }
;;
Now I create a grid of such cells.
#require "containers";;
let makegrid = CCList.init 2 ( fun i -> (CCList.init 2 (fun j -> { alive = true; column = j;row = i })) );;
I draw a square grid using lablgtk based on the number of cells in the grid.
let drawgrid area (backing:GDraw.pixmap ref) grid =
let rec loop1 limit m y =
match m with
| m when m < limit ->
(let rec loop x n =
match n with
| n when n < limit ->
let x = x + 20 in
let width, height = 20,20 in
displayrectangle area backing x y width height;
(*Printf.printf "%3d %3d\n" x y;*)
loop x (n + 1)
| n when n >= limit -> loop1 (List.length grid) (m + 1) (y + 20)
in loop 0 0)
(* when m >= limit *)
| m when m >= limit -> ()
in loop1 (List.length grid) 0 0
;;
So the final code is like this.
let makegameoflifegrid = CCList.init 7 ( fun i -> (CCList.init 7 (fun j -> { alive = false; column = j;row = i })) ) in
let drawing_area = GMisc.drawing_area ~width:200 ~height:200 ~packing:aspect_frame#add () in
drawing_area#event#connect#expose ~callback:(expose drawing_area backing);
drawing_area#event#connect#configure ~callback:(configure window backing);
drawing_area#event#add [`EXPOSURE];
window#show ();
drawgrid drawing_area backing makegameoflifegrid;
GMain.Main.main ()
;;
let _ = main ()
;;
I was wondering how to relate the cell type with its GUI representation which has x,y co-ordinates. This is basically a game of life and if I have to make a cell solid based on whether the cell is alive or not then I have to deal with two different represenations - alive attribute in the cell and x,y co-ordinates in the GUI.
Is there a functional solution for this ? The code actually works(except this problem) and has no inherent problem at this time and I know basic OCaml.
Update :
One could put the x and y co-ordinates in the record itself like this.
let drawgridrepresentation area (backing:GDraw.pixmap ref) grid =
let rec loop1 limit m y g1=
match m with
| m when m < limit ->
(let rec loop x n g=
match n with
| n when n < limit ->
let x = x + 20 in
let width, height = 20,20 in
displayrectangle area backing x y width height;
(*Printf.printf "%3d %3d\n" x y;*)
let gridmapi =
List.mapi (fun i el -> List.mapi ( fun i el1 ->
if (n = el1.column && m = el1.row)
then
({ el1 with row = x; column = y}
) else el1) el ) g in
loop x (n + 1) gridmapi
| n when n >= limit -> loop1 (List.length grid) (m + 1) (y + 20) g
in loop 0 0 g1)
(* when m >= limit *)
| m when m >= limit -> g1
in loop1 (List.length grid) 0 0 grid
;;
But I think I miss something.
Functional programming favors the application of transformations on mathematical objects. I would say that this is the main constituent of a functional thinking - a functional programmer reasons in terms of transformations, where an OOP programmer reasons in terms of objects.
The strong part of functional reasoning lies in a tight connection between it and mathematics, in particular with Category Theory and Logic, that are the foundations of mathematics.
A transformation is a relation between mathematical objects. The mathematical objects by itself are abstract, pure, and immutable. So, whenever a functional programmer (or a mathematician - that is the same) thinks about a transformation, he actually thinks about two abstractions (one to the left of the arrow, and another to the right).
If we will apply mathematical thinking to your problem, then we can express our problem as a set of abstractions. First of all, we need to speak about a Coordinate abstraction. We care only about the neighborhood relation in our Game, so I would propose the following signature for the Coordinate structure:
This is only one possible way to express this abstraction, for example this is another:
But let's stick with the
Coord
signature. Btw, notice how OCaml parlance matches the mathematics. We have OCaml structures for mathematical structures and OCaml signatures for mathematical signatures.The next abstraction is our world. Basically, it is just a collection of coordinates that we will also represent using the
fold
function (although we could choose'a list
or any other container, I would prefer not to hard-code any specific data structure).Now we have everything we need to implement our Game. From a mathematical perspective a game is just a set of rules, described with the following signature:
The implementation of rules would be a functor of the following type:
With these abstractions, we can already start to play the game, for example, choose different starting worlds and see whether the
World.step
function reaches a fixpoint (i.e., cells worldsw
andstep w
have the same states), how long does it take it to reach a fixpoint, etc.If we want to visualize, then we need to throw in more abstractions. Since we're not going to handle 3d devices, like 3d printers and hologram monitors right now, we will stick to 2d visualization. For our visualization we need a canvas abstraction, e.g.,:
We also need to handle coordinate transformations from our abstract coordinates to the Cartesian coordinates in which Canvas lives:
Finally, using these abstractions we can implement an animated game:
As you may see, with properly chosen abstractions you even don't have the problem that you were describing (i.e., the simultaneous presence of two representations of the same abstraction). So a functional way of solving your problem is not to create it :)
Bibliography
There are two essential textbooks, that teach functional programming and functional reasoning. They do not use OCaml, but Scheme, although these doesn't diminish their value, as Scheme is a pure abstraction without any syntactic sugar, that will help you to understand the essence, without blurring your mind with syntactic issues: