Search code examples
f#pattern-matchinglet-binding

Alternative approach to avoid "Incomplete pattern match" warning


I have written a function that takes an array as input and returns an array of equal size as output. For example:

myFunc [| "apple"; "orange"; "banana" |]
> val it : (string * string) [] =
    [|("red", "sphere"); ("orange", "sphere"); ("yellow", "oblong")|]

Now I want to assign the results via a let binding. For example:

let [|
        ( appleColor, appleShape );
        ( orangeColor, orangeShape );
        ( bananaColor, bananaShape )
    |] = 
    myFunc [| "apple"; "orange"; "banana" |]

Which works great...

> val orangeShape : string = "sphere"
> val orangeColor : string = "orange"
> val bananaShape : string = "oblong"
> val bananaColor : string = "yellow"
> val appleShape : string = "sphere"
> val appleColor : string = "red"

...except it produces a warning:

warning FS0025: Incomplete pattern matches on this expression. For example, the value '[|_; _; _; _|]' may indicate a case not covered by the pattern(s).

The source and reason for the warning has already been covered, I'm just looking for a succinct work-around. This function call occurs near the top of my function, and I don't like the idea of putting the entire function body inside a match:

let otherFunc =
    match myFunc [| "apple"; "orange"; "banana" |] with
    | [|
        ( appleColor, appleShape );
        ( orangeColor, orangeShape );
        ( bananaColor, bananaShape )
      |] ->
        // ... the rest of my function logic
    | _ -> failwith "Something impossible just happened!"

That just smells bad. I don't like the idea of ignoring the warning either - goes against my better judgment. Are there any other options open to me, or do I just need to find a different approach entirely?


Solution

  • One possibility if you expect this kind of calling pattern to be frequent is to make wrappers that act on the sizes of tuples you expect, e.g.

    myFunc3 (in1,in2,in3) =
        match myFunc [|in1;in2;in3|] with
        [|out1;out2;out3|] -> out1, out2, out3
        _ -> failwith "Internal error"
    

    etc. But all it does is move the ugly code to a standard place, and writing out the wrappers will be inconvenient.

    I don't think there's any better option with this API, because there's no way to tell the compiler that myFunc always returns the same number of elements it is passed.

    Another option might be to replace myFunc with an IDisposable class:

    type MyClass() =
       let expensiveResource = ...
    
    
       member this.MyFunc(v) = ...calculate something with v using expensiveResource
    
       interface IDisposable with
           override this.Dispose() = // cleanup resource
    

    and then use it in a block like

    use myClass = new MyClass()
    let appleColor, appleShape = myClass.MyFunc(apple)
    ...