# `Ash.BehaviourHelpers`
[🔗](https://github.com/ash-project/ash/blob/v3.24.3/lib/ash/behaviour_helpers.ex#L5)

Helpers for Ash behaviour modules: return-type validation and documentation of the wrapper pattern.

## Behaviour invocation pattern

Implementation modules (e.g. a module that `@behaviour Ash.Resource.Change`) should be invoked
**only through the behaviour module's public wrapper callbacks**. Call sites must call
`SomeBehaviour.callback(implementation_module, ...)` rather than
`implementation_module.callback(...)`.

Those wrappers:

* Give **Dialyzer** a single place to see the callback’s return type via `@spec`, improving static checks.
* **Enforce the return shape at runtime**: if the implementation returns a value that doesn’t match the
  behaviour’s callback return type, the wrapper raises `Ash.Error.Framework.InvalidReturnType` with a
  message that identifies the behaviour, callback (e.g. `module.function/arity`), and the allowed
  return shapes.

Use `call_and_validate_return/5` (or the `check_type!/3` macro where appropriate) inside the
behaviour’s wrapper to perform the validation.

## Dialyzer

Wrapper `@spec`s were added so that Dialyzer can check return types at call sites. Any remaining
Dialyzer warnings in the codebase (e.g. in `lib/ash/actions/action.ex`, `lib/ash/can.ex`,
`lib/ash/actions/read/read.ex`, `lib/ash/actions/read/relationships.ex`, `lib/ash/actions/update/update.ex`,
`lib/ash/actions/create/create.ex`, `lib/ash/policy/chart/mermaid.ex`) are pre-existing and not
caused by the behaviour wrapper specs.

# `call_and_validate_return`

```elixir
@spec call_and_validate_return(module(), atom(), [term()], [term()], keyword()) ::
  term()
```

Calls a callback and validates its return value against allowed patterns.

Takes the same pattern format as `check_type!/3`: atoms (exact match),
tuples (use `:_` for "any" in a position), or structs (match by struct type).
Returns the result if it matches any pattern; otherwise raises
`Ash.Error.Framework.InvalidReturnType` with a message including the
behaviour name, callback name, and allowed shapes.

## Options

* `:behaviour` - Module name of the behaviour (for error message).
* `:callback_name` or `:function` - Human-readable callback name, e.g. `"change/3"` (for error message).

## Examples

    # With valid return
    call_and_validate_return(MyMod, :change, [cs, opts, ctx], [%Ash.Changeset{}], behaviour: Ash.Resource.Change, callback_name: "change/3")
    # => %Ash.Changeset{...}

    # With invalid return (raises)
    call_and_validate_return(MyMod, :run, [], [:ok], [])
    # => raises Ash.Error.Framework.InvalidReturnType

# `check_type!`
*macro* 

---

*Consult [api-reference.md](api-reference.md) for complete listing*
