`Match.tagsTupleExhaustive` has been added
Type
- [ ] Refactor
- [x] Feature
- [ ] Bug Fix
- [ ] Optimization
- [ ] Documentation Update
Description
Match.tagsTupleExhaustive has been added
import { Either, Match, Option } from "effect"
Match.value([Option.some(1), Either.left(33)]).pipe(
Match.tagsTupleExhaustive({
NoneLeft: (none, left) => {}, // (none: None<number>, left: Left<number, never>) => void
NoneRight: (none, right) => {}, // (none: None<number>, left: Right<number, never>) => void
SomeLeft: (some, left) => {}, // (none: Some<number>, left: Left<number, never>) => void
SomeRight: (some, right) => {} // (none: Some<number>, left: Right<number, never>) => void
})
)
It also preserves rendering of union member in type hints. This is a special sugar for tagged sun-types designed to make it easier to work with a pattern in which the user wants to process all possible combinations of union members in a flat manner.
export const queenBeeState = (swarm: Swarm) => {
const whiteSurroundedQueenBee = getSurroundedQueenBee(swarm, Side.White);
const blackSurroundedQueenBee = getSurroundedQueenBee(swarm, Side.Black);
return Match.value(
distributive([whiteSurroundedQueenBee, blackSurroundedQueenBee])
).pipe(
Match.when([{ _tag: "Some" }, { _tag: "Some" }], ([white, black]) =>
QueenBeeState.BothSurrounded({ cells: [white.value, black.value] })
),
Match.when([{ _tag: "None" }, { _tag: "Some" }], ([_white, black]) =>
QueenBeeState.OneSurrounded({ cell: black.value })
),
Match.when([{ _tag: "Some" }, { _tag: "None" }], ([white, _black]) =>
QueenBeeState.OneSurrounded({ cell: white.value })
),
Match.when([{ _tag: "None" }, { _tag: "None" }], ([_white, _black]) =>
QueenBeeState.Free()
),
Match.exhaustive
);
};
The function is not limited to a pair and can take a tuple of arbitrary length.
Related
- Related Issue #
- Closes #
🦋 Changeset detected
Latest commit: 35e0ac3cae0e7697874d66150a5861a5d21f0457
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 32 packages
| Name | Type |
|---|---|
| effect | Minor |
| @effect/cli | Major |
| @effect/cluster | Major |
| @effect/experimental | Major |
| @effect/opentelemetry | Major |
| @effect/platform-browser | Major |
| @effect/platform-bun | Major |
| @effect/platform-node-shared | Major |
| @effect/platform-node | Major |
| @effect/platform | Major |
| @effect/printer-ansi | Major |
| @effect/printer | Major |
| @effect/rpc | Major |
| @effect/sql-clickhouse | Major |
| @effect/sql-d1 | Major |
| @effect/sql-drizzle | Major |
| @effect/sql-kysely | Major |
| @effect/sql-libsql | Major |
| @effect/sql-mssql | Major |
| @effect/sql-mysql2 | Major |
| @effect/sql-pg | Major |
| @effect/sql-sqlite-bun | Major |
| @effect/sql-sqlite-do | Major |
| @effect/sql-sqlite-node | Major |
| @effect/sql-sqlite-react-native | Major |
| @effect/sql-sqlite-wasm | Major |
| @effect/sql | Major |
| @effect/typeclass | Major |
| @effect/vitest | Major |
| @effect/ai | Major |
| @effect/ai-anthropic | Major |
| @effect/ai-openai | Major |
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
But maybe would be better to introduce Tagged.ts module with functions for type Tagged = {_tag: string}?
@mikearnaldi what do you think?
@mikearnaldi Hi! I'm sorry if I'm distracting you, but could you give me some feedback? What do you think about this feature?
On one hand, using multiple concatenated tags as keys seems a bit strange. But I think the fact that tags are strings can be used to achieve more compact code.
It feels a little too specific to be included. You can do things like this already which gets pretty close:
import { Either, Match, Option } from "effect"
Match.value([Option.some(1), Either.left(33)]).pipe(
Match.when([Option.isSome, Either.isLeft], ([some, left]) => {}),
Match.when([Option.isNone, Either.isLeft], ([none, left]) => {}),
Match.when([Option.isSome, Either.isRight], ([some, right]) => {}),
Match.when([Option.isNone, Either.isRight], ([none, right]) => {}),
Match.orElseAbsurd,
)
I agree – but still, this option doesn't ensure completeness (Match.exhaustive doesn't work) and is more verbose.
Maybe it makes sense to include a helper like this then? With it, Match.exhaustive will work.
I think we could simply call it distibutive and overload it to work with both tuples and structs – what do you think?
https://effect.website/play#335756a0cfb9
import { Either, Match, Option } from "effect"
export type Distributive<
I extends ReadonlyArray<any>,
Result extends ReadonlyArray<any> = []
> = I extends readonly [infer T, ...infer Rest extends ReadonlyArray<any>]
? [T] extends [never] ? Distributive<Rest, [...Result, T]>
: T extends infer TMember ? Distributive<Rest, [...Result, TMember]>
: never
: Result
export const distributiveTuple = <const T extends ReadonlyArray<any>>(
value: T
): Distributive<T> => {
return value as any as Distributive<T>
}
Match.value(
distributiveTuple([Option.some(1), Either.left(33)]) // <- distributiveTuple
).pipe(
Match.when([Option.isSome, Either.isLeft], ([some, left]) => {}),
Match.when([Option.isNone, Either.isLeft], ([none, left]) => {}),
Match.when([Option.isSome, Either.isRight], ([some, right]) => {}),
Match.when([Option.isNone, Either.isRight], ([none, right]) => {}),
Match.exhaustive
)
Probably what really needs changing then is improving the exhaustiveness checks.
@tim-smart What do you mean? Should we always consider the input data in a distributive way?
The match exclusion logic should be updated so this is exhaustive by default.