80 lines
2.2 KiB
Markdown
80 lines
2.2 KiB
Markdown
|
|
# Haskell Applicative Validation
|
||
|
|
|
||
|
|
This note covers `28-haskell-applicative-validation/`, which validates a release manifest with a custom `Validation` type that accumulates several
|
||
|
|
field errors at once.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. Why This Is Not Just `Either`
|
||
|
|
|
||
|
|
`Either` stops at the first failure. That is useful for many workflows, but it is not always the best fit for validating a form-like input.
|
||
|
|
|
||
|
|
This example wants a different behavior:
|
||
|
|
|
||
|
|
- validate `service`,
|
||
|
|
- validate `env`,
|
||
|
|
- validate `owners`,
|
||
|
|
- validate `replicas`,
|
||
|
|
- validate `strategy`, and
|
||
|
|
- validate `window`,
|
||
|
|
|
||
|
|
and report every invalid field in one pass.
|
||
|
|
|
||
|
|
That is why the example uses a `Validation` type with an `Applicative` instance that combines errors through `Semigroup`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Where the Accumulation Happens
|
||
|
|
|
||
|
|
The important function is:
|
||
|
|
|
||
|
|
```haskell
|
||
|
|
validateManifest :: RawManifest -> Validation [String] ReleaseManifest
|
||
|
|
```
|
||
|
|
|
||
|
|
It builds the final manifest with applicative style:
|
||
|
|
|
||
|
|
```haskell
|
||
|
|
ReleaseManifest
|
||
|
|
<$> validateService ...
|
||
|
|
<*> validateEnvironment ...
|
||
|
|
<*> validateOwners ...
|
||
|
|
<*> validateReplicas ...
|
||
|
|
<*> validateStrategy ...
|
||
|
|
<*> validateWindow ...
|
||
|
|
```
|
||
|
|
|
||
|
|
Each field validator can fail independently, and the `Applicative` instance combines all the resulting error lists.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. Why the Input Is Split into Two Stages
|
||
|
|
|
||
|
|
The example still parses `key=value` assignments with `Either`.
|
||
|
|
|
||
|
|
That keeps syntax errors separate from semantic validation:
|
||
|
|
|
||
|
|
- malformed assignments such as `service` or `owners=` fail immediately, and
|
||
|
|
- well-shaped assignments become a `RawManifest` that the validation layer can inspect field by field.
|
||
|
|
|
||
|
|
This separation keeps the example focused. The interesting part is accumulated validation, not ad hoc string parsing.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Commands to Try
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd 28-haskell-applicative-validation
|
||
|
|
|
||
|
|
nix develop
|
||
|
|
cabal run
|
||
|
|
cabal run -- service=api-gateway env=production owners=platform,security replicas=3 strategy=canary:20 window=22-24
|
||
|
|
cabal test
|
||
|
|
|
||
|
|
nix build
|
||
|
|
./result/bin/mini-validation service=api-gateway env=production owners=platform,security replicas=3 strategy=canary:20 window=22-24
|
||
|
|
|
||
|
|
nix run . -- service=api-gateway env=production owners=platform,security replicas=3 strategy=canary:20 window=22-24
|
||
|
|
nix flake check
|
||
|
|
```
|