> For the complete documentation index, see [llms.txt](https://rdeneau.gitbook.io/safe-clean-architecture/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://rdeneau.gitbook.io/safe-clean-architecture/tips-and-tricks/tips-and-tricks/as-active-pattern.md).

# As active pattern

🏷️ `#ActivePattern`

The `As` active pattern performs a type test and cast, inspired by C#'s `as` operator:

```fsharp
let inline (|As|_|) (input: obj) : 't option =
    match input with
    | :? 't as value -> Some value
    | _ -> None
```

F# has a built-in type test pattern (`:?`), but it cannot be **nested** inside other patterns — it requires a separate `match` expression or a guard clause. `As` solves this by wrapping the type test in a partial active pattern, making it composable with any other pattern.

## Use case: `BusinessError(As OrderCannotBeCancelledAfterShipping)`

In Shopfoo, the common `Error` union wraps business errors behind an `IBusinessError` interface:

```fsharp
[<Interface>]
type IBusinessError =
    abstract member Code: string
    abstract member Message: string

type Error =
    | BusinessError of IBusinessError
    | Bug of exn
    // ...
```

Each domain defines its own error type implementing this interface:

```fsharp
type OrderError =
    | OrderCannotBeCancelledAfterShipping
    | OrderTransitionForbidden of current: OrderStatus * attempted: OrderStatus
    interface IBusinessError with
        override this.Code = ...
        override this.Message = ...
```

Since `BusinessError` holds an `IBusinessError`, we cannot directly pattern-match on the concrete `OrderError` case. `As` solves this by enabling deep pattern matching across multiple layers. Consider this saga undo predicate:

```fsharp
type UndoCriteria = { WorkflowError: Error; History: ProgramStep list }

let canUndoExceptAfterShipOrder undoCriteria =
    match undoCriteria with
    | { WorkflowError = BusinessError(As OrderCannotBeCancelledAfterShipping) } -> false
    | _ -> true
```

The pattern drills through **four nesting levels** in a single expression:

1. `{ WorkflowError = ... }` — destructures the `UndoCriteria` record
2. `BusinessError(...)` — matches the `BusinessError` case of the `Error` union
3. `As ...` — type-tests the `IBusinessError` interface to the concrete `OrderError` type
4. `OrderCannotBeCancelledAfterShipping` — matches the specific `OrderError` case

Without `As`, this would require a guard clause or nested `match` expressions to perform the intermediate type test.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://rdeneau.gitbook.io/safe-clean-architecture/tips-and-tricks/tips-and-tricks/as-active-pattern.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
