---
title: "Behavioral Contracts on S7"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Behavioral Contracts on S7}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
knitr::read_chunk(
  system.file("examples", "vector-laws.R", package = "s7contract")
)
```

```{r setup}
library(S7)
library(s7contract)
```

`s7contract` describes what a consumer needs from an S7 object and tests whether
implementations behave as expected. S7 provides class definitions, method
registration, and dispatch.

Structural interfaces and explicit traits address the need described in the
[S7 traits discussion](https://github.com/RConsortium/S7/issues/34):
checking method contracts around existing generics.
[Go interfaces](https://go.dev/ref/spec#Interface_types) inform the structural
approach; [Rust traits](https://doc.rust-lang.org/reference/items/traits.html)
inform explicit registrations, defaults, and associated metadata. Here these
are runtime R facilities. Checked calls and generative laws extend them from
method availability to evidence about behavior.

| Mechanism | Question |
|:--|:--|
| S7 properties and validators | Is the object's representation valid? |
| `implements()` | Can S7 find the required methods? |
| `has_trait()` | Has this implementation been declared? |
| `with()` / `%::%` | Do this call's arguments and return value satisfy their specifications? |
| `check_law()` | Does a behavioral claim hold over the generated cases? |

## A vector protocol

A windowing function needs length, slicing, and access to values. `VectorLike`
states those requirements. Both double vectors and `ReadDepth` objects provide
the methods; the `ReadDepth` validator keeps positions and depths aligned.

```{r vector-interface}
```

The consumer uses the protocol without depending on either representation:

```{r vector-consumer}
```

`assert_implements()` checks method availability. Inside `with(VectorLike, ...)`,
calls also check the argument and return specifications declared by the
interface. For example, its slice operation requires integer indices:

```{r checked-indices}
tryCatch(
  with(VectorLike, vec_slice(coverage, "first")),
  error = function(e) conditionMessage(e)
)
```

## Declaring an implementation

Use a trait when a declaration or associated metadata matters to the consumer.
Here the declaration attaches measurement units to `ReadDepth`:

```{r measurement-trait}
Measured <- new_trait("Measured",
  methods = list(values = trait_method(vec_values)),
  assoc_consts = "UNITS"
)
has_trait(ReadDepth, Measured)

impl_trait(Measured, ReadDepth,
  methods = list(values = function(x) x@depth),
  assoc_consts = list(UNITS = "reads"),
  replace = TRUE
)
has_trait(ReadDepth, Measured)
trait_assoc_const(Measured, ReadDepth, "UNITS")
```

## Testing behavior

Method availability and valid return types leave semantic claims untested.
This law checks length against the values used to construct the object:

```{r length-law}
length_law <- new_law("length matches constructor input",
  generators = list(values = gen_vector(gen_double(-10, 10), max = 6L)),
  holds = function(values) {
    x <- ReadDepth(position = seq_along(values), depth = values)
    with(VectorLike, identical(vec_length(x), base::length(values)))
  }
)
check_law(length_law, tests = 100L, seed = 1L)
```

The [vector law suite](protocol-laws.html) runs four laws against both
representations and finds a faulty slice method that still satisfies the
interface. Its generators preserve valid objects and indices while shrinking.

For generator composition, replay, and tinytest integration, see
[Generative Laws with tinytest](property-laws.html). The
[Maybe dictionary](monad-dictionaries.html) shows function-valued operations;
[Testing Stateful S7 Protocols](stateful-protocols.html) covers sequences of
mutations checked against a reference model.
