0
votes

I am using patternmatching within a function as follows :

let rec calculate   : Calculation<MyType> -> MyVO -> Calculation<MyType list>=
    fun c l ->
        fun arrayA arrayC ->
        myComputationExpression {
            if arrayA.Length<>arrayC.Length then 
                yield! l
            else
                match arrayA, arrayC with
                | [], [] -> yield! C
                | [a], [c] -> yield! calculate a c
                | headA :: tailA, headC :: tailC -> 
                    yield! calculateOpenAny headA headC
                    yield! calculate c l tailA tailC
        }

I have the following warning :

Fsc: C:\myfile.fs(17,27): warning FS0025: Incomplete pattern matches on this expression. For example, the value '( _ , [_] )' may indicate a case not covered by the pattern(s).

I quite don't get it because, it should not happen becuse of the first conditionnal statement :

if arrayA.Length<>arrayC.Length then 

Is there something I am missing here, or could I overlook the warning?

1

1 Answers

3
votes

The problem is that the compiler is not all-seeing. It can prove some things, but it cannot analyse your program fully, paying attention to semantics, like a human could.

In particular, the compiler doesn't know the relationship between the value of property .Length and the shape of the list (i.e. whether it's empty or not). From the compiler's point of view, Length is just a random property, you might as well have compared the results of .ToString() calls.

A better way to go about this would be incorporating different-lengths case in the pattern match:

let rec calculate   : Calculation<MyType> -> MyVO -> Calculation<MyType list>=
    fun c l ->
        fun arrayA arrayC ->
        myComputationExpression {
            match arrayA, arrayC with
            | [], [] -> yield! C
            | [a], [c] -> yield! calculate a c
            | headA :: tailA, headC :: tailC -> 
                yield! calculateOpenAny headA headC
                yield! calculate c l tailA tailC
            | _, _ -> yield! l
        }

An additional bonus here would be performance: computing length of a list is actually an O(n) operation, so you'd do better by avoiding it.


On an unrelated note, keep in mind that all of these are equivalent:

fun a -> fun b -> fun c -> ...
fun a -> fun b c -> ...
fun a b -> fun c -> ...
fun a b c -> ...

This is because of how parameters work in F# (and in all of ML family): functions take parameters "one by one", not all at once, at each step returning another function that "expects" the rest of parameters. This is called "currying". Functions in F# are curried by default.

In your case this means that your function could be declared shorter like this:

let rec calculate =
    fun c l arrayA arrayC ->
        ...

Or even shorter like this:

let rec calculate c l arrayA arrayC =
   ...