Let us now move to the development a basic grader. We ask the student
to implement the identity function and provide the following
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
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 *)  (* List of tested inputs *) let () = set_result @@ ast_sanity_check code_ast @@ fun () -> [exercise_1]
Test_lib.test_function_1_against_solution is the key
here. Let us take a moment to understand how it is called:
[%ty: int -> int] is written in a PPX extension of
OCaml: it reifies the type
int -> int as a first-class value. With
that information in its hands,
can check that the student has written a function of the right type.
"identity" is the identifier of the function to be tested.
The optional argument
gen is set to
0 because we are not willing to
automatically generate inputs. (This will be the topic of another step
of this tutorial.)
The final argument
 is the unique input on which we want to test
This function also determines the text written in the header of the
corresponding report. For a function called
my_function, it will be
Copy this exercise source to your own exercise directory.
learn-ocaml build && learn-ocaml serve
http://localhost:8080 in your browser.
Check that the template does not get the point.
Modify the code, get your point!
Change the argument
gen:41 for instance and
rebuild your learn-ocaml instance.
Grade your answer and observe the effect of the previous change. This is the topic of the next step of this tutorial!
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:
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).
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).