Dang! I kept saying to myself: "Don't forget the bracket"!, and of course I forgot!
I'm glad you got it to work Ian.
So, what happens?
Each record in the form passes the value it has for ID (lConID) and Surname (strSurname) and the form (frm) itself to the function.
You pass the form object to the function so that the function can access it's RecordsetClone (which is a DAO recordset).
Then the function finds the record with the same contact ID using: '.FindFirst "ConID = " & lConID'
It then moves back to the previous record: '.MovePrevious'
It checks that you haven't moved back before the first record (ie you were on the first record): 'If Not .BOF Then'
Then comes the interesting part! Compare the first letter of the surname in the previous record with the first letter of the passed surname:
Left(.Fields("ConSurname", 1)) <> Left(strSurname, 1)
This evaluates to either True or False which is assigned as the result of the function:
fNewStartLetter = Left(.Fields("ConSurname", 1)) <> Left(strSurname, 1)
You can encase it in brackets if it makes it clearer:
fNewStartLetter = (Left(.Fields("ConSurname", 1)) <> Left(strSurname, 1))
If the result of the 'Not .BOF' check returned False (ie there is no previous record to compare) then the above comparison is skipped and the function returns False (which is what a boolean variable initialises to.)
I'm not sure if that is any clearer - but the main thing is that is does the job!

d