let rec nth (list, i) = match list, i with | x::xs, 0 -> x | x::xs, i -> nth(xs, i-1) | [], _ -> ()
This function will indeed have the (unwanted) signature you mentioned:
val nth : unit list * int -> unit
Why? Look at the right side of these three rules. If this is not for () , you cannot say what specific type of value your function returns. But as soon as you add the last rule, F # sees the expression () (which has a unit type), and from it you can infer the return type of your function; which is now no longer common. Since any function can have only one fixed return type, it infers that x , xs also includes the unit type, with the result that the signature is higher.
As kvb already noted, you want to sometimes return a value, but in the sheet of an empty input list you don’t want to return anything ... that is, your return value should be 'a option (can also be written as option<'a> btw.)
let rec nth (list, i) = match list, i with | x::xs, 0 -> Some(x) | x::xs, i -> nth(xs, i-1)
Now the reporting signature looks right:
val nth : 'a list * int -> 'a option
Regarding your second question , I admit that I cannot fully answer it because I'm still a F # newbie. One hint: if you take the above function in the correct form (the general version returns 'a option ), you actually cannot help but check all the possible return values:
Why? Since, if you want to get the actual return value (with the name x code just shown), you need to "extract" it using the match block:
let result = nth (someList, someIndex) match result with | Some(x) -> ... | None -> ...
And since your rules should always be comprehensive (so that the compiler does not complain), a rule will automatically be added to you that checks the possibility of None .
The compiler will actually make you also think about what should happen in a “bug” situation; you cannot forget about it. You just choose how to handle it!
(Of course, once you program .NET and deal with types that may be null , things may look a little different, since null not a native concept of F #.)
Further suggestion for improvement:. If you change the way your nth function takes its arguments, you get the opportunity to partially apply it, which means, for example, that you can use it with the pipeline operator:
let rec nth i list =
Now you can do this:
someList |> nth someIndex
Or that:
let third = nth 2 someList |> third
If, on the other hand, your function only accepts a tuple that will not work. Therefore, consider whether you really need the tuple parameter: in this case, it actually limits the flexibility, and, moreover, the "meaning" / content of the two parameters does not imply that they should always be stored and displayed together. Therefore, I would advise against using a tuple in this case.