Skip to content

Commit f89ea8f

Browse files
CopilotBillWagner
andauthored
Clarify difference between type annotation and type test patterns in F# documentation (#49260)
* Initial plan * Clarify difference between type annotation and type test patterns Co-authored-by: BillWagner <[email protected]> * Apply suggestions from code review * Add ai-usage frontmatter metadata Co-authored-by: BillWagner <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: BillWagner <[email protected]> Co-authored-by: Bill Wagner <[email protected]>
1 parent 86a6a1d commit f89ea8f

File tree

1 file changed

+61
-3
lines changed

1 file changed

+61
-3
lines changed

docs/fsharp/language-reference/pattern-matching.md

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,30 @@ The following code shows some additional uses of the wildcard pattern:
185185

186186
## Patterns That Have Type Annotations
187187

188-
Patterns can have type annotations. These behave like other type annotations and guide inference like other type annotations. Parentheses are required around type annotations in patterns. The following code shows a pattern that has a type annotation.
188+
Patterns can have type annotations. These behave like other type annotations and guide inference like other type annotations. Parentheses are required around type annotations in patterns.
189+
190+
A pattern with a type annotation uses the syntax `pattern : type` and provides **compile-time type information** to the type checker. This is purely a static type annotation that helps with type inference - it doesn't perform any runtime type checking or conversion. The compiler uses this information during compilation to determine the type of the pattern variable.
191+
192+
The following code shows a pattern that has a type annotation:
189193

190194
[!code-fsharp[Main](~/samples/snippets/fsharp/lang-ref-2/snippet4815.fs)]
191195

196+
In this example, `(var1 : int)` tells the compiler that `var1` is of type `int`. This is resolved at compile time, and the generated code treats `var1` as an integer throughout the match expression. This pattern will match any integer value and bind it to `var1`.
197+
198+
**Key characteristics:**
199+
200+
- Uses the syntax `pattern : type` (with a single colon).
201+
- Resolved at **compile time** - provides type information to the type checker.
202+
- Does not perform runtime type testing.
203+
- Used for type inference and to guide the compiler.
204+
192205
## Type Test Pattern
193206

194-
The type test pattern is used to match the input against a type. If the input type is a match to (or a derived type of) the type specified in the pattern, the match succeeds.
207+
The type test pattern is used to match the input against a type **at runtime**. If the input type is a match to (or a derived type of) the type specified in the pattern, the match succeeds.
195208

196-
The following example demonstrates the type test pattern.
209+
A type test pattern uses the syntax `:? type` and performs **runtime type checking**, similar to the `is` or `as` operators in C#. This pattern tests whether a value is of a specific type during program execution, making it useful when working with inheritance hierarchies or interface implementations.
210+
211+
The following example demonstrates the type test pattern:
197212

198213
[!code-fsharp[Main](~/samples/snippets/fsharp/lang-ref-2/snippet4816.fs)]
199214

@@ -211,6 +226,49 @@ let m (a: A) =
211226
| _ -> ()
212227
```
213228

229+
**Key characteristics:**
230+
231+
- Uses the syntax `:? type` or `:? type as identifier` (with a question mark).
232+
- Resolved at **runtime** - performs actual type checking during execution.
233+
- Tests if a value is an instance of a specific type or its derived types.
234+
- Commonly used with inheritance hierarchies and polymorphic types.
235+
- Similar to C#'s `is` operator or `as` operator.
236+
237+
### Contrasting Type Annotations and Type Test Patterns
238+
239+
While both patterns involve types, they serve very different purposes:
240+
241+
| Feature | Type Annotation Pattern (`pattern : type`) | Type Test Pattern (`:? type`) |
242+
|---------|-------------------------------------------|-------------------------------|
243+
| **Syntax** | Single colon: `a : int` | Colon with question mark: `:? Button` |
244+
| **When resolved** | Compile time | Runtime |
245+
| **Purpose** | Guides type inference | Tests actual type of value |
246+
| **Use case** | Helping the compiler understand types | Checking runtime types in inheritance hierarchies |
247+
| **Equivalent in C#** | Type annotations in switch patterns | `is` or `as` operators |
248+
249+
The following example demonstrates the differences:
250+
251+
```fsharp
252+
// Type annotation pattern - compile time
253+
let detect1 x =
254+
match x with
255+
| 1 -> printfn "Found a 1!"
256+
| (var1 : int) -> printfn "%d" var1
257+
// The ': int' tells the compiler var1 is an int
258+
// Everything is resolved at compile time
259+
260+
// Type test pattern - runtime
261+
type A() = class end
262+
type B() = inherit A()
263+
264+
let test (a: A) =
265+
match a with
266+
| :? B -> printfn "Runtime check: it's a B"
267+
| _ -> printfn "Runtime check: it's not a B"
268+
// The ':? B' performs a runtime type check
269+
// The actual type is tested during execution
270+
```
271+
214272
## Null Pattern
215273

216274
The null pattern matches the null value that can appear when you are working with types that allow a null value. Null patterns are frequently used when interoperating with .NET Framework code. For example, the return value of a .NET API might be the input to a `match` expression. You can control program flow based on whether the return value is null, and also on other characteristics of the returned value. You can use the null pattern to prevent null values from propagating to the rest of your program.

0 commit comments

Comments
 (0)