learn-ocaml

Step 2: Basic grading by comparison with your solution

Let us now move to the development a basic grader. We ask the student to implement the identity function and provide the following template.ml:

let identity x = "Put your code here"

Our goal is to check that this function is modified by the student in such a way that it behaves as our solution.ml:

let identity x = x

To that end, the grader will compare the outputs of the student function with the function of the solution. We must provide inputs of a ground type (i.e. of non polymorphic type), execute the two functions and compare their outputs. Each time the outputs match, the student gets one point.

Assuming that we choose int for this ground type, the behavior described in the previous paragraph is implemented as follows:

open Test_lib
open Report

let exercise_1 =
  test_function_1_against_solution
    [%ty: int -> int] (* Type of the tested function *)
    "identity"        (* Identifier of the tested function *)
    ~gen:0            (* Number of automatically generated tests *)
    [0]               (* List of tested inputs *)

let () =
  set_result @@
  ast_sanity_check code_ast @@ fun () ->
  [exercise_1]

The function Test_lib.test_function_1_against_solution is the key here. Let us take a moment to understand how it is called:

This function also determines the text written in the header of the corresponding report. For a function called my_function, it will be “Function: my_function”.

Do it yourself!

  1. Copy this exercise source to your own exercise directory.

  2. Update your index.json file.

  3. Run learn-ocaml build && learn-ocaml serve

  4. Open http://localhost:8080 in your browser.

  5. Check that the template does not get the point.

  6. Modify the code, get your point!

  7. Change the argument ~gen:0 to gen:41 for instance and rebuild your learn-ocaml instance.

  8. Grade your answer and observe the effect of the previous change. This is the topic of the next step of this tutorial!

Want to learn more about grading function ?

The next steps will bring you progressively to understand most of the possibilities of grading functions. However, if you want to have a better overview right now, you can go directly to step 5 where you will:

Multiple arguments

To grade a function with multiple arguments you simply need to use the corresponding grading function which follows this pattern : Test_lib.test_function_<function arity>_against_solution and give the inputs as n-uplets:

open Test_lib
open Report

let exercise_1 =
  test_function_2_against_solution
    [%ty: int -> int -> int] "op"
    ~gen:5
    [ (1,2) ; (0,1) ]

let () =
  set_result @@
  ast_sanity_check code_ast @@ fun () ->
  [ exercise_1 ]

You can find this example in the exercises/grade-function-multiple_args directory (branch: step-2).

Polymorphic functions : testing several types

For a polymorphic functions, you may want to test the function with different types. To do so, you can concat the result of numerous grading functions and encapsulate it in a Section which has two arguments : some text and a list of items produced by grading functions.

open Test_lib
open Report

let exercise_1 =
    Section ([ Text "Function: "; Code "identity" ; Text " with multiple tested input types." ],
             [ test_function_1_against_solution
               [%ty: int -> int] (* [identity] tested with integer *)
               "identity"
               ~gen:0 [1 ; 2];
               test_function_1_against_solution
                 [%ty: char -> char] (* [identity] tested with char *)
                 "identity"
                 ~gen:0 ['c' ; 'a'];
               test_function_1_against_solution
                 [%ty: float -> float] (* [identity] tested with float *)
                 "identity"
                 ~gen:0 [1.1 ; 2.4]]
            )

let () =
  set_result @@
  ast_sanity_check code_ast @@ fun () ->
  [ exercise_1 ]

You can find this example in the exercises/grade-function-polymorphism directory (branch: step-2).