These — One Value or Both
1.93 kB minified + gzipped
A temperature sensor can return a usable reading together with a note that it drifted outside its calibrated range. A service that treats every response as either valid or failed must discard one of the two facts: either the measurement or the maintenance signal. Neither loss is visible in the response.
Core Behavior
Section titled “Core Behavior”These<A, B> represents a value that is first, second, or both, with no built-in success or failure
meaning.
Firstholds only the first value,Secondonly the second, andBothholds both. There is no empty variant.mapFirstandmapSecondcall their transform only when that side exists and preserve the other side.mapBothtransforms each side that exists.chainFirstonBothreturns the callback’sTheseand drops the existing second value;Secondpasses through untouched.chainSecondmirrors this behavior.matchhandles all three variants;to.Pairproduces a pair only forBoth.
Choose These when both pieces are data the caller needs, not when one is an error list.
Result keeps one success value or one error, so it cannot return a usable
reading with a warning. The choosing guide compares the containers side by
side.
Example: Sensor Readings
Section titled “Example: Sensor Readings”A temperature reading may be valid but outside the sensor’s expected range, or it may be invalid and
unusable. These.make.both(reading, warning) carries a usable reading and its warning together.
When a reading and a warning are both present, mapFirst changes the reading and keeps the warning,
while mapSecond changes the warning and keeps the reading. The example formats both:
flowchart TD
Input["These(reading, diagnostic)"] --> Kind{"Which variant?"}
Kind -->|First| Format["mapFirst formats the reading"]
Kind -->|Second| KeepSecond["mapFirst skips; mapSecond prefixes the diagnostic"]
Kind -->|Both| FormatBoth["Both transforms run"]
Format --> First["First(formatted reading)"]
KeepSecond --> Second["Second(prefixed diagnostic)"]
FormatBoth --> Both["Both(formatted reading, prefixed diagnostic)"]
If the input is Second, mapFirst skips its callback and mapSecond transforms the diagnostic.
chainFirst returns the callback’s entire These, replacing the original reading and its warning
on Both; chainSecond behaves symmetrically, replacing a coexisting first value with its
callback’s result.
Parsing and Displaying Readings
Section titled “Parsing and Displaying Readings”This parser returns a diagnostic for invalid input and adds a warning to high but valid readings:
The parser chooses the variant from the value. The transformations then act on whichever sides
exist, and match is the only place that has to handle all three shapes.
Sending the Reading and Its Note Together
Section titled “Sending the Reading and Its Note Together”Downstream telemetry expects one payload containing both facts. to.Pair extracts a pair only from
Both; Pair.merge gives the two values names:
A reading with no note, or a note with no reading, becomes None here because the downstream
payload requires both. Pair supplies the named conversion; it carries no success
or failure semantics of its own.
Core Operations
Section titled “Core Operations”Creating and Combining Values
Section titled “Creating and Combining Values”| Signature | What it does | Return type |
|---|---|---|
These.make.first(value) / make.second(value) |
Creates a single-sided value. | First<A> / Second<B> |
These.make.both(first, second) |
Keeps both values. | Both<A, B> |
These.from.Pair(pair) |
Converts a pair into Both. |
Both<A, B> |
Transforming Values
Section titled “Transforming Values”| Signature | What it does | Guarantee |
|---|---|---|
These.mapFirst(fn) / mapSecond(fn) |
Transforms the selected side when present. | Preserves the other side. |
These.mapBoth(onFirst, onSecond) |
Transforms each present side. | Keeps the same variant. |
These.chainFirst(fn) / chainSecond(fn) |
Replaces the value with the callback’s These when the selected side is present. |
A coexisting opposite side is discarded; an input with only that opposite side passes through. |
These.swap(value) |
Exchanges first and second. | Both stays Both. |
These.tapFirst(fn) / tapSecond(fn) / tapBoth(fn) |
Runs a callback when the requested side or sides are present. | Returns the original value. |
Reading and Converting
Section titled “Reading and Converting”| Signature | What it does | Return type |
|---|---|---|
These.match(cases) |
Handles first, second, and both. |
The selected callback result. |
These.is.first(value) / is.second(value) / is.both(value) |
Checks the exact variant. | Type guard. |
These.hasFirst(value) / hasSecond(value) |
Checks for a side, including Both. |
Type guard. |
These.getFirstOrElse(fn) / getSecondOrElse(fn) |
Reads a side or calls a fallback when absent. | Value or fallback result. |
These.to.Pair(value) |
Extracts both values when present. | Some(pair) for Both, otherwise None. |
Failure Behavior and Edge Cases
Section titled “Failure Behavior and Edge Cases”If both a reading and a diagnostic can be absent, represent that outside These, for example with
Maybe<These<Reading, Diagnostic>>. These has no empty variant and no way to attach an error to a
missing side.
chainFirst on Both returns the callback’s result and drops the existing second value. For
example, the warning is lost when the callback returns only a formatted reading:
chainSecond similarly replaces a coexisting first value. Use mapping when both sides must survive.
tapBoth runs only for Both, while tapFirst and tapSecond also run for the single-sided
variants that contain their side.