oPolo
oPolo

Reputation: 576

F# odd pattern matching issues

While writing some code yesterday, I ran into two odd problems, which neither me nor my functional programming oriented friend could figure out. We have looked at it for quite some time, and researched it on the net, but we were not able to find any answers anywhere, so here goes:

The issue is that in this code:

First weird problem:

let outer1 (bs : byte array) =
    let rec inner (bs : byte array) (bacc : byte array) (i : int) =
        match i with
        | bs.Length -> bacc // <--- Error: bs is not recognized. Why?
        | _ -> bacc.[i] <- bs.[i]
               inner bs bacc (i + 1)
    inner bs (Array.zeroCreate bs.Length) 0

The problem here is: FS0039: The namespace or module 'bs' is not defined. How can this be? bs is in the function signature after all. Moreover, defining a new value with let bsLength = bs.Length works right before the match. But by doing so I see a new oddity:

let outer2 (bs : byte array) =
    let rec inner (bs : byte array) (bacc : byte array) (i : int) =
        let bsLength = bs.Length
        match i with
        | bsLength -> bacc
        | _ -> bacc.[i] <- bs.[i] // <--- Warning: Rule never matched. Why?
               inner bs bacc (i + 1)
    inner bs (Array.zeroCreate bs.Length) 0

Here the problem is a warning that says: warning FS0026: This rule will never be matched. I don't get that at all. i and the length of the array has no relation to each other. If I write an integer (for instance 10) instead of bsLength, the warning disappears.

Upvotes: 3

Views: 542

Answers (2)

Gene Belitski
Gene Belitski

Reputation: 10350

Both your problems stem from the expectation that pattern matching allows using values and literals interchangeably. No, it does not. Pattern Matching (F#) topic on MSDN gives a good overview of supported pattern types and precedence rules of their application. The major principle simplifying a lengthy description there is: you cannot match a value unless this value is a literal, or identifier (a case value of a discriminated union, an exception label, or an active pattern case).

In your first problem point compiler treats bs.Length not as a property Length of array bs as you expect, but as a literal or identifier Length from non-existing module or namespace bs; as John Palmer pointed in his answer you may achieve the expected behavior by using variable pattern with a guard statement. A sample of legitimate use of the pattern matching expression resembling yours would be:

module bs =
    [<Literal>]
    let Length = 100
//.............................
let v = 100;
let s = match v with
    | bs.Length -> "matched"
    | _ -> "not matched";;

val s : string = "matched"

The second problem point is treated by compiler as variable pattern, and bsLength is assigned a value of i instead of values being compared, as you expected; second matching rule does not have chances to kick in.

Upvotes: 8

John Palmer
John Palmer

Reputation: 25526

The match statement doesn't work like you think it does - the correct syntax is

match i with
| t when t = bs.Length

In the second case, you actually create a new variable called bsLength which hides the definition of the earlier bsLength and matches all integers, so you get the rule never matched warning.

Upvotes: 6

Related Questions