--- title: "A Maybe Dictionary on S7" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{A Maybe Dictionary on S7} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") knitr::read_chunk( system.file("examples", "maybe-laws.R", package = "s7contract") ) ``` ```{r maybe-setup} ``` This S7 dictionary stores `pure` and `bind` as functions. `Just(value)` carries a value; `Nothing()` represents absence. An interface checks operation availability, while laws state the required behavior. ## The dictionary `pure` wraps a value in `Just`. `bind` passes a `Just` payload to a function returning Maybe, and propagates `Nothing` without calling that function. ```{r maybe-dictionary} ``` Binding `Nothing` leaves the second callback unevaluated: ```{r maybe-use} ``` ## Generated values and functions The input domain is `Nothing` or `Just` containing one integer from -10 to 10, with equal constructor probabilities. Functions come from three equally likely families: always return `Nothing`, add an integer from -5 to 5, or retain values at least a generated threshold in that range. Integer ranges expand with size. These functions are total on the tested inputs and intermediate values, which stay between -20 and 20. ```{r maybe-domain} ``` Function descriptions remain data in counterexamples. Shrinking tries earlier constructors and function families, then moves integer parameters toward zero. The interpreter constructs each function independently of the dictionary under test. Equality compares the observable constructor and uses `identical()` for payloads, so integer and double payloads differ. ## Three laws, one suite The [Haskell 2010 Report, ยง6.3.6](https://www.haskell.org/onlinereport/haskell2010/haskellch6.html) gives the three monad equations. Writing `pure` for the unit operation and `>>=` for bind: ```text pure(a) >>= f = f(a) m >>= pure = m (m >>= f) >>= g = m >>= (x -> f(x) >>= g) ``` `maybe_laws(dictionary)` returns three ordinary laws. Each equation uses the same interpreted functions on both sides. Coverage labels describe reference outcomes of those functions, including absence at the input or after either function in a composition. ```{r maybe-law-suite} ``` ```{r maybe-checks} ``` These laws test the generated families of functions and integer payloads. `dict_bind()` calls the dictionary under test; `gen_bind()` composes generators and their shrink trees. ## A default that breaks the laws This dictionary replaces every `Nothing` result with `Just(0L)`. Its operations still satisfy the structural interface: ```{r maybe-broken} ``` Associativity fails because the default allows a later function to run on one side of the equation. The reduced input keeps both function descriptions: ```{r maybe-counterexample} ``` The same law and recorded parameters reproduce the failure: ```{r maybe-replay} ``` The [Haskell monad tutorial](https://www.haskell.org/tutorial/monads.html) develops the distinction between type-class operations and their laws.