Let this types =
type intC = int;;
type boolC = bool;
type stringC = string;;
type component = A of intC | B of boolC | C of stringC;;
If I want to apply a function on the type a of a component A, do I need systematically to deconstruct the component ?
for exemple do i have to do :
let add comp =
match comp with
| A i -> Some (i + 2) (*only A interests me, I return i + 2*)
| _ -> None (*otherwise I return nothing*)
and then for any function on a component A ? Is there any mean to avoid thy redundancy ?
It really depends which kind of operation you will perform on your types.
The solution given by @nlucaroni is perfectly fine, but if you want to do something a bit more generic (and complex) you can use a record to hold your partial map functions:
type 'a component_m = {
a : intC -> 'a;
b : boolC -> 'a;
c : stringC -> 'a;
}
let map_component m = function
| A a -> m.a a
| B b -> m.b b
| C c -> m.c c
let add = map_component {
a = (fun x -> Some (x + 2));
b = (fun _ -> None);
c = (fun _ -> None);
}
If you don't want to have to write the (fun _ -> None)
function every time, you can also use a default value that you extend:
let none = {
a = (fun _ -> None);
b = (fun _ -> None);
c = (fun _ -> None);
}
let add = map_component { none with a = fun x -> Some (x+2) }
You can do the same things with functors but this in my opinion this becomes overkill.
You can pass a higher-order-function to a function that does the component destruction for you,
let apply_if_a f = function
| A i -> Some (f i)
| (B _ | C _) -> None
The type for this would be,
val apply_if_a : (int -> 'a) -> component -> 'a option
As you can see, polymorphic for any function applied to any value of A
. Also, most people stay away from the catch-all, _
, and to be exhaustive instead.