# filepather

Computations that depend on a file path, and named build steps that record
the files they make.
## `FilePather`
```haskell
newtype FilePather f a = FilePather (FilePath -> f a)
type FilePather' a = FilePather Identity a
```
A `FilePather` is isomorphic to `ReaderT FilePath f a`, with the same
instances, and `ask` is the file path being operated on.
`System.FilePather.Combinators` builds larger `FilePather` values from
smaller ones:
* `p ~> q` runs `q` on the file path that `p` produces, and `p ~>> q` runs `q`
on each of the file paths that `p` produces.
* `onFile` runs on another file path, and `hoistFilePather` changes `f`.
* A `Rule f a` is a `FilePather` that applies to some file paths and not
others. Make one with `rule predicate x`, and combine rules with `<|>`,
where the first rule that applies is used.
* `everyFile directoryFiles r` applies the rule `r` to every file in the
directory being operated on.
## `Tracked`
```haskell
newtype Tracked f a = Tracked (FilePather (ReaderT Trail (ExceptT Cycle f)) (Outputs, a))
type Outputs = [(FilePath, Step)]
```
A `Tracked` value is a build step, or several, run on a file path with the
`Trail` of that file: the `Step`s that built it, nearest first, back to its
source file. It produces its result and the files it made, each with the step
that made it.
* `track name outputs x` names the `FilePather` `x`, which makes the files
`outputs` gives for its input. Before `x` runs, the step is checked against
the trail: a step whose name is already in the trail (`CycleRepeatsStep`),
or that would make a file already in the trail (`CycleReproducesInput`), is
stopped with a `Cycle`, and `x` does not run.
* `track' name x` names a `FilePather` that returns the files it made, for a
step whose outputs are known only after it runs. It is checked after it
runs.
* `trackWith Repeatable` allows a step to appear more than once in a trail.
* `whenPath`, `whenTrail`, `onPath`, `askTrail` and `untracked` choose
files, and run `FilePather` values that make no files.
* `displayCycle` describes the chain of a `Cycle`:
```
src/a.md → markdown to pdf → public/a.pdf → pdf page 1 → public/a_page1.pdf → pdf page 1 again
```
`Tracked` is separate from `FilePather`, so that `FilePather` keeps all of its
instances (`Distributive`, `Representable`, `Selective`, `Decidable`, and the
`MonadError` and `MonadWriter` of `f`). `Tracked` has `Functor`, `Apply`,
`Applicative`, `Bind`, `Monad`, `Selective`, `Alt`, `Plus`, `Alternative`,
`MonadPlus`, `Semigroup`, `Monoid`, `Filterable`, `MonadTrans`, `MonadIO`,
`MonadFail`, `MonadReader FilePath`, `MonadState`, `MonadError`,
`MonadWriter` and `Contravariant` instances. `<>` runs both steps and keeps
the outputs of both; `<|>` and `catchError` alternate in `f`; and a `Cycle`
is never caught or discarded by any of them.
## Optics
Every data type has a name for each of its fields and constructors: a class
whose method is an optic, of the kind that each data type gives it (the
classy optics recipe of
[claude-haskell](https://github.com/sirius-beta/claude-haskell)). Each name
is in its own module under `System.FilePather.Name`, and each data type also
has its own name (`step`, `trail`, `cycle`, `filePather`, `tracked`). A data
type that holds another inherits its names, so `stepName` reaches the name of
the step in a `Step`, a `ReproducesInput`, or a `Cycle`:
```haskell
view (cloneLens stepName) (Step "pdf page 1" "a.pdf") -- a lens
preview stepName (CycleRepeatsStep (RepeatsStep s t)) -- an affine traversal
view (cloneLens pathFunction) (asks length :: FilePather' Int) "notes.md"
```
Read one value with `view (cloneLens n)`, at most one with `preview n`, and
set with `set` and `over`. `System.FilePather` exports `cycle`, the name of
`Cycle`, so hide `Prelude.cycle` where both are used.
## Development
This package is built and tested with
[document-build](https://gitlab.com/tonymorris/document-build), which
uses it:
```
cabal build all
cabal test all
bin/lint.sh --check
```