In the case of user-defined types, it is mandatory to define a
sampler. Both two previous methods (using the `~sampler`

optional
argument or defining a sampler function `sample_my_type`

) can be used
but required a little more work, especially for parametric types.

For non-parametric type, it is exactly the same than in the previous step.

You can find the examples below in the `exercises/sampler-user-defined-types`

directory (branch: step-4).

In the examples, we use the type `color`

defined as :

```
type color = Green | Yellow | Red | Blue
```

`~sampler`

argumentAs in the previous step, you can add the argument `~sampler`

of type
`unit -> <arg1_type> * <arg2_type> * <arg3_type> * etc.`

```
let exercise_1 =
test_function_1_against_solution
[%ty: color -> string] "color_to_string"
~sampler: (fun () -> match Random.int 4 with
| 0 -> Red | 1 -> Green | 2 -> Yellow | _ -> Blue)
~gen:5
[]
```

You can also define your own sampler and not use the `~sampler`

argument with the following rule: a sampler of type `unit -> my_type`

has to be named `sample_my_type`

.

```
let sample_color () : color =
match Random.int 4 with
| 0 -> Red
| 1 -> Green
| 2 -> Yellow
| _ -> Blue
let exercise_2 =
test_function_1_against_solution
[%ty: color -> string] "color_to_string"
~gen:5
[]
```

In this case, the grader will automatically use your sampler
`sample_color`

for the type `color`

. Be careful to write
`sample_color`

and not `sampler_color`

.

You can find the examples below in the
`exercises/sampler-user-defined-parametric-types`

directory (branch: step-4).

In the examples below, we use the types:

```
type col = R | B
type 'a tree =
| Leaf
| Node of 'a tree * 'a * 'a tree
```

`~sampler`

argumentNo change here, just don’t forget that the optional argument`~sampler`

has type `unit -> <arg1_type> * <arg2_type> * <arg3_type> * etc.`

.

```
let sample_col () = match Random.int 2 with
| 0 -> B
| _ -> R
let sample_col_tree () =
let rec builder h = match h with
| 0 -> Leaf
| n -> match Random.int 3 with
| 0 -> Leaf
| _ -> Node (builder (h-1), sample_col (), builder (h-1))
in
let h = Random.int 5 + 2 in
builder h
let exercise_1 =
test_function_2_against_solution
[%ty: col tree -> col -> col tree] "monochrome"
~sampler:(fun () -> sample_col_tree (), sample_col ())
~gen:5
[]
```

A sampler of a parametric type `('a * 'b * ... ) my_type`

has a
type : ```
(unit -> 'a) -> (unit -> 'b) -> ... -> -> (unit -> ('a * 'b *
...) my_type
```

and must be named `sample_my_type`

.

So for example, if we want to test a function of type `col tree -> int`

, so we
need two samplers :

```
(* Not a parametric type *)
let sample_col () = match Random.int 2 with
| 0 -> B
| _ -> R
(* A parametric type *)
let sample_tree (sample: unit -> 'a) : unit -> 'a tree =
let rec builder h = match h with
| 0 -> Leaf
| n -> match Random.int 3 with
| 0 -> Leaf
| _ -> Node (builder (h-1), sample (), builder (h-1))
in
let h = Random.int 5 + 2 in
fun () -> builder h
```

The grading function is then simply :

```
let exercise_2 =
test_function_1_against_solution
[%ty: col tree -> int] "height"
~gen:5
[]
```

Note that if instead of [col tree], the input type is [int tree] (or another type with a predefined sampler), you need nothing more.

```
let exercise_2bis =
test_function_1_against_solution
[%ty: int tree -> int] "height"
~gen:5
[]
```

With these two samplers, we are also able, without more effort, to
grade a function of type `col tree -> col -> col tree`

for
example. The grader is simply:

```
let exercise_3 =
test_function_2_against_solution
[%ty: col tree -> col -> col tree] "monochrome"
~gen:5
[]
```

More advanced examples (but nothing new) can be found in
`exercises/advanced-examples-step-4`

directory (branch: step-4).

There is nothing new in these examples, only more complexed types, in particular examples for functional types graded with both methods and using the predefined sampler of list.

The user-defined type is:

```
type position = {x: int ; y: int}
```

and its corresponding sampler:

```
let sample_position () = { x=sample_int () ; y=sample_int () }
```

`get_x`

Exactly as shown previously, using method 2:

```
let exercise_1 =
test_function_1_against_solution
[%ty: position -> int ]
"get_x"
~gen:5
[{ x=0 ; y=0 }]
```

`map`

(functional input type)We want to grade the function ‘map’ for ‘int list’ so we need a sampler for function of type ‘int -> int’.

This is not possible to use the naming convention for a functional type without an alias (see method 2).

```
let sampler_fun () = match Random.int 3 with
| 0 -> succ
| 1 -> pred
| _ -> fun x -> if x < 0 then -1 else 1
```

For this method, we can just build the proper sampler for all the function arguments.

```
let sampler_2 () =
(sampler_fun (), sample_list ~min_size:1 ~max_size:10 sample_int ())
let exercise_2 =
test_function_2_against_solution
[%ty: (int -> int) -> int list -> int list ] "map"
~sampler:sampler_2
~gen:5
[(succ, [])]
```

For this method, we need to use an alias for type `int -> int`

.

```
type f_int_int = int -> int
let sample_f_int_int = sampler_fun
let exercise_2bis =
test_function_2_against_solution
[%ty: f_int_int -> int list -> int list ] "map"
~gen:5
[]
```

In case you want to grade a function with a tuple as an input type, you can either use method 1 or define an alias and use method 2.

```
let exercise_3 =
test_function_1_against_solution
[%ty: int * int -> int ] "first_elt"
~sampler: (fun () -> sample_int (), sample_int ())
~gen:5
[]
```

```
type pair_int = int * int
let sample_pair_int () = sample_int (), sample_int ()
let exercise_3bis =
test_function_1_against_solution
[%ty: pair_int -> int ] "first_elt"
~gen:5
[]
```

Both methods work well for a lot of exercises. However for functional
types and tuples, you will need do give an alias to your types to be able
do use the second method (see the examples in `advanced_examples`

).
This is useful if you need to grade several functions that share some
input types.