GregRos
GregRos

Reputation: 9113

Confused regarding active patterns

I understand how active patterns can be defined and used in F#, including partial active patterns, and the different sorts of patterns available. E.g.

let (|Big|Small|) animal = if animal.IsBig then Big(animal) else Small(animal)
let f = function | Big(_) -> "big" |Small(_) -> "small

However, I'm confused regarding using active patterns and discriminated unions in let bindings, arguments, and other places. For example, the MSDN has the following code:

let GetSubstring1 (Slice(p0, p1, text)) = 
    printfn "Data begins at %d and ends at %d in string %s" p0 p1 text
    text.[p0..p1]

Which confuses me a bit.

A specific issue. Let's say I have the discriminated union,

type Union = A of int * int | B of int

Can I somehow make a function that accepts only Union.A, e.g.

let f (A(a, b)) = a + b

In this situation, it tells me there are unmatched patterns. Is there a way to satisfy it?

Upvotes: 3

Views: 345

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243051

As noted by @ildjarn, this applies to all patterns. They can appear in cases of the match clause (and in function which is similar), but also in the parameter declaration of let bound function and even in the let value binding.

The key difference is that with let, you only want to use complete patterns that will always succeed. This is not needed for match or function, because there are multiple clauses, so if the first one fails, the matching can continue and try the next one.

For example, the following complete pattern takes int and returns it as a string:

let (|AsString|) (n:int) = n.ToString()

Here are some ways how you can use it:

let (AsString s) = 42         // Defines a value 's' of type string
match 3 with AsString s -> s  // Returns a value "3" of type string
let convert (AsString s) = s  // Defines a function 'int -> string'
let convert = function AsString s -> s  // Same as the previous line

EDIT: To answer your second question, if you use let with an incomplete pattern (i.e. that accepts only a single case of a discriminated union) then you get a compiler warning and the code may fail at runtime (if you call it with the other discriminated union case):

let f (A(a, b)) = a + b      // This gives compile-time warning
f (B 42)                     // This will fail at runtime 

If you need to define a function that works only one of the cases, then you need to define a separate type.

type AInfo = int * int 
type Union = A of AInfo | B of int  

Then you can write a function that will only take AInfo (but you can still work with Union in places where both options represent a valid input).

Upvotes: 8

Related Questions