We're planting a tree for every job application! Click here to learn more

Modules, abstraction and tests - afl, alcotest, Compiler warnings - Fundamentals

dinkar ganti

21 Dec 2022

3 min read

Modules, abstraction and tests - afl, alcotest, Compiler warnings - Fundamentals
  • OCaml

Some aspects of developing applications in OCaml that I find helpful

Between tests and compiler warnings quite a few bugs and typos can be eliminated from an implementation. There are still a few issues with, for example, handling floating point math in the design that I need to address; something that I will write about once I have that module in place. The code snippets shown here are part of ocal and for anyone interested can follow that repository.

Inline tests

Dune supports inline_tests through ppx_inline_test. I used to fall in the test code should be separate from the main code as tests could trigger some unintended consequences. However, when I started using inline tests I could see the power of using inline tests.

 (inline_tests)
 (preprocess (pps ppx_inline_test))

Here is how it looks for one of the modules

  let poly (x : real) (polyList : real list) =
    List.fold_left (fun acc ele -> (acc *. x) +. ele) 0.0 polyList

  let epoch : int = 0

  let rd (tee : int) = tee - epoch

  let%test _ =
    let p0 = poly 1.0 ([1.0; 2.0; 3.0]) in
    let expected = 6.0 in
    expected = p0

  let%test _ = epoch = 0

Testing is a big topic; it is nice to have the support of these tools to make our life a bit easier. There are some disadvantages of the above approach and the let%test expressions are not necessarily the preferred way of running tests because, in my opinion, they do clutter the application space.

Testing alternatives - Fuzzing, Alco, and, QuickCheck

The test below are wrapped in a binary that depends on the library that we need to test. These tests cover variant types with Crowbar's generators and there is a test case using the Alcotest library.

let dayofweek () =
  add_test ~name:"dayofweek" [ day_of_week_gen ] @@ function
  | d ->
      let i = Calendar.int_of_day d in
      let d2 = Calendar.day_from_int i in
      check_eq ~pp:Calendar.pp_day d d2

let months () =
  add_test ~name:"months" [months_gen] @@ function
    | m ->
      let i = G.int_from_month_type m in
      let m2 = G.month_type_from_int i in
      check_eq ~pp:G.pp_month m m2

let binary_search_test () =
  Alcotest.(check @@ float epsilon_float) 
      "Test bisection" (CTLibrary.to_float @@ CTLibrary.binary_search (CTLibrary.from_float 0.) (CTLibrary.from_float 1.) 
              (fun (x, y) -> 
                  ((CTLibrary.to_float x) -. (CTLibrary.to_float y)) < epsilon_float)
              (fun r -> r < (CTLibrary.from_float 1.))) 0.5
  
let () =
  binary_search_test ();
  dayofweek ();
  months ();

In the above tests, there are some simple generators that generate tests for variant types and there is also an instance of a Alcotest case that checks for expected results.

Abstract Types (a digression)

The test case for binary_search demonstrates the need to create conversion routines for an abstract type, because if such functions are not available the compiler will throw the following error

File "test/ocal_fuzzer.ml", line 43, characters 70-75:
43 |       "Test bisection" (CTLibrary.to_float @@ CTLibrary.binary_search ( 0.) (CTLibrary.from_float 1.) 
                                                                           ^^^^^
Error: This expression has type float but an expression was expected of type
         CTLibrary.real

module type Library = sig
  type real
  val from_float : float -> real
  val to_float : real -> float
  val binary_search :
    real -> real -> (real * real -> bool) -> (real -> bool) -> real
  (** Bisection search for x in lo to hi such that end holds, test determines when to go left.
     binarySearch l lo h hi x test end. *)
end

module Library : Library = struct
  type real = float
  let to_float r = r
  let from_float r = r
  let binary_search (lo : real) (hi : real)
        (predicate : real * real -> bool) (endCondition : real -> bool) = failwith "Implementation removed"
 end

Unused value warning - some more ocaml

Ignoring warning -32 is not necessarily a good idea.

Consider the following module.

module type JulianCalendar = sig
  module JD = CT.Date
  val jd_epoch : JD.fixed_date
  end
 module JulianCalendar : JulianCalendar = struct
module JulianCalendar : JulianCalendar = struct
  let jd_epoch = JD.rd @@ JD.fixed_date_from_int @@ int_of_float @@ -1721424.5
  let jd_epoch = failwith "test"

 end

In the above example there was a typo and the same function signature is being duplicated. In this example, since

let jd_epoch = failwith "test"

is the last definition of jd_epoch, which is the definition that will get picked up at runtime. The above unused value warning catches an exception that would prevent one to even use dune utop to come for the module.
I had almost decided to ignore this warning in the file, obviously not a wise thing to do with warnings.

Conclusion

As I am getting my feet wet with OCaml and its rich module interface, I find these warnings and tests to help keep the design in check. There are still a few issues in the design that I need to handle specially when some calendars are ok with integer types whereas others are using float (floating point computations present their own challenges, that would need a separate post.)

Did you like this article?

dinkar ganti

A demo app using libreoffice scripting framework: https://www.youtube.com/watch?v=INT6duJM7X8

See other articles by dinkar

Related jobs

See all

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Title

The company

  • Remote

Related articles

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

12 Sep 2021

JavaScript Functional Style Made Simple

JavaScript Functional Style Made Simple

Daniel Boros

12 Sep 2021

WorksHub

CareersCompaniesSitemapFunctional WorksBlockchain WorksJavaScript WorksAI WorksGolang WorksJava WorksPython WorksRemote Works
hello@works-hub.com

Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ

108 E 16th Street, New York, NY 10003

Subscribe to our newsletter

Join over 111,000 others and get access to exclusive content, job opportunities and more!

© 2023 WorksHub

Privacy PolicyDeveloped by WorksHub