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.