Skip to content

Commit d62e40d

Browse files
CopilotBillWagner
andcommitted
Clarify difference between type annotation and type test patterns
Co-authored-by: BillWagner <[email protected]>
1 parent cfad86b commit d62e40d

File tree

1 file changed

+59
-3
lines changed

1 file changed

+59
-3
lines changed

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

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,15 +184,29 @@ The following code shows some additional uses of the wildcard pattern:
184184

185185
## Patterns That Have Type Annotations
186186

187-
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.
187+
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.
188+
189+
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.
190+
191+
The following code shows a pattern that has a type annotation:
188192

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

195+
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`.
196+
197+
**Key characteristics:**
198+
- Uses the syntax `pattern : type` (with a single colon)
199+
- Resolved at **compile time** - provides type information to the type checker
200+
- Does not perform runtime type testing
201+
- Used for type inference and to guide the compiler
202+
191203
## Type Test Pattern
192204

193-
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.
205+
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.
206+
207+
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.
194208

195-
The following example demonstrates the type test pattern.
209+
The following example demonstrates the type test pattern:
196210

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

@@ -210,6 +224,48 @@ let m (a: A) =
210224
| _ -> ()
211225
```
212226

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

215271
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)