The F# collections modules define the find
function. The find operation traverses a collection and returns the first item in the collection that meets the given condition(s).
Perhaps you are a teacher. You have a type that represents a student with a name and a grade:
type Student(name: string, grade: int) = member x.Name = name member x.Grade = grade override x.ToString() = sprintf "(%s, %d)" name grade
And you have a randomized list of those students:
let students = [ Student("Sally", 79); Student("Giedrius", 81); Student("Aryana", 98); Student("Ty", 94); Student("Eloise", 86); Student("Vergil", 89); Student("Doug", 66); Student("Delmar", 77); Student("Makenna", 88); Student("Orval", 93) ]
Maybe you want to reward a random A-student (i.e., a student with a grade of 90 or above) with a candy bar. You can use find
to return the first A-student in the list:
let scholar = students |> List.find (fun s -> s.Grade >= 90) // val scholar : Student = (Aryana, 98)
So, that’s great: Aryana gets a Twix! (It is the only one with the cookie crunch, after all.)
You also happen to be one of those sadistic teachers, though, who likes to embarrass the students who are not living up to their potential. Therefore you pick a random F-student (i.e., with a grade less than 65) and outfit him/her with a dunce cap:
let dunce = students |> List.find (fun s -> s.Grade < 65) // An unhandled exception of type // 'System.Collections.Generic.KeyNotFoundException' // occurred in FSharp.Core.dll
Whoa! Exception? Where did that come from? (Who’s the dunce now?)
That’s a shortcoming of find
. If no element meets the condition, find
throws an exception.
Enter tryFind
. The tryFind
function returns an Option
. Even on an empty collection, tryFind
does not throw an exception.
let dunceOpt = students |> List.tryFind (fun s -> s.Grade < 65) // val dunceOpt : Student option = None
Of course, you may give a test one week that slays the entire class: you may not have an A-student that week. You’d better use tryFind
to find an A-student, too:
let scholarOpt = students |> List.tryFind (fun s -> s.Grade >= 90) // val scholarOpt : Student option = // Some (Aryana, 98)
Now you can test dunceOpt
and scholarOpt
before you use them to make sure someone is supposed to get a candy bar or a dunce cap before you start handing them out.