ca9163d9
ca9163d9

Reputation: 29159

This expression was expected to have type xxxx but here has type unit

The following code has a type error for both of the printfns.

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

Answers (1)

Ganesh Sittampalam
Ganesh Sittampalam

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

Related Questions