Skip to content

Commit 14fd729

Browse files
Add a dev doc for option modifiers
1 parent 93163b9 commit 14fd729

File tree

1 file changed

+71
-0
lines changed

1 file changed

+71
-0
lines changed

docs/Developer/option-modifiers.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Option Modifiers
2+
3+
Instead of exposing internal record structures, this library
4+
uses **Option Modifiers**: functions of type `Config -> Config`
5+
(endomorphisms) that are composed to build a final configuration.
6+
7+
## Advantages
8+
9+
* **Clean Composition:** Use the standard `.` operator to chain multiple
10+
options together seamlessly.
11+
* **Encapsulation:** The internal record structure and field names
12+
remain hidden, preventing breaking changes if the implementation shifts.
13+
* **Order Independence:** Unlike positional arguments, the order of
14+
modifiers in a chain does not affect the final result (unless a specific
15+
option is overridden).
16+
* **Unified Interface:** Removes the need for record syntax or multiple
17+
constructor patterns. There is only one way to specify options: through
18+
functions.
19+
* **Bundling:** Common configurations can be pre-composed into a single
20+
named modifier (e.g., `standardRecursive = withRecursive FollowTopLink .
21+
withForce True`).
22+
* **Discipline:** By limiting the "surface area" of the API, we reduce
23+
the possibility of misuse or invalid state transitions.
24+
25+
## Disadvantages
26+
27+
* **Discovery:** Users cannot simply "see" the available options via a
28+
record definition; they must rely on module documentation.
29+
30+
* **Introspection:** It is difficult to inspect a composed modifier
31+
chain to see what it "contains" before applying it to a base
32+
configuration.
33+
34+
## Modifiers and Setters
35+
36+
We use the prefix `with` for option modifiers (e.g., `withRecursive`,
37+
`withForce`).
38+
39+
**Why `with`?**
40+
Unlike `set`, which implies an imperative "action" or a binary toggle,
41+
`with` conveys a functional transformation. It suggests that the
42+
resulting operation will be performed *with* a specific property or
43+
value, regardless of the previous state.
44+
45+
## Expressing All Possible Values
46+
47+
A modifier must be **total**. It should not merely "toggle" a default;
48+
it must allow the user to explicitly define the desired state.
49+
50+
If a user receives a pre-composed bundle of modifiers, they may not know
51+
the current state of a specific option. To ensure predictable behavior,
52+
the modifier must allow them to force a value (e.g., `withVerbose True`
53+
or `withVerbose False`), ensuring the final config matches their intent
54+
regardless of the input chain.
55+
56+
## Reset to Default
57+
58+
While the base configuration starts with library defaults, we provide
59+
`resetOption` functions (e.g., `resetRecursive`).
60+
61+
These are useful when you want to "neutralize" an option within a
62+
specific composition. For example, if you have a `standardConfig` bundle
63+
that includes recursion, but for one specific call you want to ensure it
64+
is disabled, you can simply append `. resetRecursive` to the chain.
65+
66+
## Summary
67+
68+
By using composed endomorphisms, we provide a declarative DSL for
69+
filesystem operations. This approach balances the flexibility of a
70+
record with the safety and elegance of functional composition, covering
71+
all use cases while maintaining a strict, predictable API.

0 commit comments

Comments
 (0)