Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F#, how far is it reasonable to go when checking for valid arguments?

I'm trying to learn a little of the mindset of functional programming in F#, so any tips are appreciated. Right now I'm making a simple recursive function which takes a list and returns the i:th element.

let rec nth(list, i) =
    match (list, i) with
    | (x::xs, 0) -> x
    | (x::xs, i) -> nth(xs, i-1)

The function itself seems to work, but it warns me about an incomplete pattern. I'm not sure what to return when I match the empty list in this case, since if I for example do the following:

| ([], _) -> ()

The whole function is treated like a function that takes a unit as argument. I want it to treat is as a polymorphic function.

While I'm at it, I may as well ask how far is reasonable to go to check for valid arguments when designing a function when developing seriously. Should I check for everything, so "misuse" of the function is prevented? In the above example I could for example specify the function to try to access an element in the list that is larger than its size. I hope my question isn't too confusing :)

like image 948
Alex Avatar asked Nov 12 '10 14:11

Alex


1 Answers

You can learn a lot about the "usual" library design by looking at the standard F# libraries. There is already a function that does what you want called List.nth, but even if you're implementing this as an exercise, you can check how the function behaves:

> List.nth [ 1 .. 3 ] 10;;
System.ArgumentException: The index was outside the range 
  of elements in the list. Parameter name: index

The function throws System.ArgumentException with some additional information about the exception, so that users can easily find out what went wrong. To implement the same functionality, you can use the invalidArg function:

| _ -> invalidArg "index" "Index is out of range."

This is probably better than just using failwith which throws a more general exception. When using invalidArg, users can check for a specific type of exceptions.

As kvb noted, another option is to return option 'a. Many standard library functions provide both a version that returns option and a version that throws an exception. For example List.pick and List.tryPick. So, maybe a good design in your case would be to have two functions - nth and tryNth.

like image 186
Tomas Petricek Avatar answered Nov 15 '22 11:11

Tomas Petricek