Reputation: 29159
The following code has a type error for both of the printfn
s.
How should I fix it? I'd like the Seq.map
loop to continue even if one of the items fails.
// files should be a Seq of record
let files = seqOfStrs |> Seq.map(fun s ->
match s with
| Helper.ParseRegex "(\w+) xxxxx" month ->
let currentMonth = .....
if currentMonth = month.[0] then
doc.Load(shelf)
// parse doc and return record type. Omitted
else
printfn "Expect %s found %s." currentMonth month.[0] //Error
| _ ->
printfn "No '(Month) Payment Data On Line' prompt." //Error
Upvotes: 1
Views: 3363
Reputation: 29100
The problem is that the different branches of your logic are producing different things. In the "omitted" section you're apparently returning a value of type xxxx (e.g. string
), but in the other branches with the printfn
calls, you're returning without a value, which in F# is represented as unit
and in other languages is often called void
.
If you didn't want the loop to continue, the simplest answer would be to just throw an exception in those cases, e.g:
failwithf "Expect %s found %s." currentMonth month.[0]
The static return type of failwithf
can be any type at all, including string
, because it never actually returns so doesn't actually have to produce a value of that type.
Given that you do want your code to continue, you could instead return an empty string or some kind of failure value, e.g.:
printfn "Expect %s found %s." currentMonth month.[0] //Error
"failed"
Now all the branches have the same type so your code will compile, but you have to be careful that callers don't accidentally interpret this value as being some valid result, which is why throwing an exception is generally cleaner.
A cleaner approach is to use an option
value to represent success or failure, i.e. return Some "..."
in the correct case and None
in the error cases. This approach is nicer when the failure is a common-place occurrence that the calling code would like to handle rather than just reporting the problem to the user and aborting:
// files should be a Seq of record
let files = seqOfStrs |> Seq.map(fun s ->
match s with
| Helper.ParseRegex "(\w+) xxxxx" month ->
let currentMonth = .....
if currentMonth = month.[0] then
doc.Load(shelf)
// parse doc and produce record type.
Some record
else
printfn "Expect %s found %s." currentMonth month.[0] //Error
None
| _ ->
printfn "No '(Month) Payment Data On Line' prompt." //Error
None
You now need to decide what you want files
to contain at that point - do you want the explicit None
value to show that something failed, or do you just want the sequence to contain the correct values and omit the failures entirely?
If you want the explicit values, then you can just leave the option
values in place and you'll have a seq<string option>
result. Note that this type is just syntactic sugar for seq<option<string>>
, reflecting F#'s O'Caml heritage.
If you just want to omit them, then replace your Seq.map
call with Seq.choose
, which will drop all the None
values and strip off the Some
wrappers, leaving you with seq<string>
as your current code is trying to produce.
Upvotes: 7