F# How to write a function that takes int list or string list

You are running into value restrictions on the automatic generalization of the type inference system as outlined here


Case 4: Adding type parameters.

The solution is to make your function generic rather than just making its parameters generic.

let inline contains5< ^T when ^T : (static member op_Explicit: ^T -> int) > (xs : ^T list)  =
    List.map int xs
    |> List.contains 5

You have to make the function inline because you have to use a statically resolved type parameter, and you have to use a statically resolved type parameter in order to use member constraints to specify that the type must be convertible to an int. As outlined here

You can use inline to prevent the function from being fixed to a particular type.

In FSI, the interactive REPL:

> open System;;
> let inline contains5 xs = List.map int xs |> List.contains 5;;
val inline contains5 :
  xs: ^a list -> bool when  ^a : (static member op_Explicit :  ^a -> int)

> [1;2;3] |> contains5;;
val it : bool = false

> ["1";"2";"5"] |> contains5;;
val it : bool = true

Note that the signature of contains5 has a generic element to it. There's more about inline functions here.