let map f list =
let rec loop acc = function
| [] -> List.rev acc
| head :: tail -> loop (f head :: acc) tail
loop [] list
The signature of this function is ('a -> 'b) -> 'a list -> 'b list, which is the most generic it can be. This does not prevent 'a from being the same type as being 'b, but it also allows them to be different. Here you can see that the 'a type that is the parameter to the function f must match the type of the list parameter. This function is still generic, but there are some slight constraints on the inputs - if the types don't match, there will be a compile error.
Examples:
> let map f list = ...
val it : ('a -> 'b) -> 'a list -> 'b list
> map (fun x -> float x * 1.5) [1; 2; 3; 4];;
val it : float list = [1.5; 3.0; 4.5; 6.0]
> map (sprintf "abc%.1f") [1.5; 3.0; 4.5; 6.0];;
val it : string list = ["abc1.5"; "abc3.0"; "abc4.5"; "abc6.0"]
> map (fun x -> x + 1) [1.0; 2.0; 3.0];;
error FS0001: The type 'float' does not match the type 'int'