How to split main and the rest of your code with Ocaml?

80 views Asked by At

I was wondering how could I achieve the following:

  • have all the "business logic" in a file
  • have a main.ml that uses that business logic

Business logic:

type point = {x:float; y:float;}

let pi_known = 3.141592653589793238462643383279502884197169399375105820974944592307816406286

let percentage_error pi_estimated =
  ((pi_known -. pi_estimated) /. pi_known) *. 100.0

let pi_and_error pi error =
  Printf.sprintf "Pi's value :: %.10f Error rate :: %.10f %.%" pi error

let point_to_string { x = x; y = y } =
  Printf.sprintf "%.2f %.2f" x y

let gen_point xr yr =
  {x=xr; y=yr}

let distance_between_points p q =
  (p.x -. q.x) *. (p.x -. q.x) +. (p.y -. q.y) *. (p.y -. q.y)

let distance_from_origin c =
  distance_between_points c (gen_point 0.0 0.0)

let count_within ~counter:n =
  let rec count_within_aux ~counter:n ~within:m =
    match n, m with
      | 0, m -> m
      | n, m ->
        let cc = gen_point (Random.float 1.0) (Random.float 1.0) in
        let dist = distance_from_origin cc in
        match dist with
          | dist when dist <= 1.0 -> count_within_aux ~counter:(n - 1) ~within:(m + 1)
          | dist when dist > 1.0 -> count_within_aux ~counter:(n - 1) ~within:m
          | _ -> 0 in
  count_within_aux ~counter:n ~within:0

let count_within_stepping ~counter:n ~stepping:s =
  let rec count_within_stepping_aux ~counter:n ~within:m ~acc:acc =
    match n, m, acc with
      | n, m, acc when n <= 0 -> m
      | n, m, acc ->
          let c = count_within s in
          let pi = ((float_of_int m) /. (float_of_int acc)) *. 4.0 in
          let r = percentage_error pi in
          print_endline (pi_and_error pi r);
          count_within_stepping_aux ~counter:(n-s) ~within:(m+c) ~acc:(acc+s) in
  count_within_stepping_aux ~counter:n ~within:0 ~acc:0

pi.mli:

(*
 * Point in a two-dimensional Euclidean space
 *)
type point = {x:float; y:float;}
val point_to_string : point -> string
val gen_point : float -> float -> point
(*
 * 'Euclidean distance or Euclidean metric is the "ordinary" straight-line distance between
 * two points in Euclidean space. With this distance, Euclidean space becomes a metric space.
 * The associated norm is called the Euclidean norm.
 * Older literature refers to the metric as Pythagorean metric.'
 * https://en.wikipedia.org/wiki/Euclidean_distance
 *)
val distance_between_points : point -> point -> float
val distance_from_origin : point -> float
val count_within : counter:int -> int
val count_within_stepping : counter:int -> stepping:int -> int
val percentage_error : float -> float
val pi_and_error : float -> float -> string

main.ml:

let main () =
  Random.self_init();
  let num_iter = Sys.argv.(1) in
  let n = int_of_string num_iter in
  print_endline ("Number of iterations :: " ^ num_iter);
  let pi_estimated = ((float_of_int (Pi.count_within_stepping n (n / 20))) /. (float_of_int n)) *. 4.0 in
  let r = Pi.percentage_error pi_estimated in
  print_endline (Pi.pi_and_error pi_estimated r)

let () =
  main ()

_oasis:

Name: Pi
Version: 0.1
Synopsis: Nope
Authors:
  Istvan <[email protected]>
License: MIT
Homepage: http://0.0.0.0
OASISFormat: 0.4
BuildTools: ocamlbuild
Plugins: META (0.4), DevFiles (0.4)
Executable "pi"
  Path: src
  MainIs: main.ml
  CompiledObject: best
  BuildDepends:
    str,unix

Unfortunatelly when I compile this and run it it returns nothing while if I merge main.ml and pi.ml it works as expected. What am I missing?

UPDTE:

After adding the mli file to the project and changing main.ml as it was suggested by @gallais it works as expected.

1

There are 1 answers

0
Istvan On

It turns out that mli files are necessary for using modules. Having src/x.ml and src/x.mli is required. If these files are present X can be referenced from main.ml.