Currently match
and wildMatch
support only matching one single union type.
If a match needs to be done on several types, nesting becomes very painful and requires a lot of boilerplate.
type A = 'A' | 'B' | 'C'
type F = 'F' | 'G' | 'H'
const matchTyple = ([a, f]: [A, F]) => match<A, string>({
A: () =>
wildMatch<F, string>({
F: () => 1,
_: () => 2,
})(f),
B: () =>
wildMatch<F, string>({
G: () => 3,
_: () => 4,
})(f),
C: () =>
wildMatch<F, string>({
H: () => 5,
_: () => 6,
})(f),
})(a)
Here is how we would write that in Haskell
data A = A | B | C
data F = F | G | H
-- pattern match using `case x of`
matchTyple :: A -> F -> Int
matchTyple a f =
case (a, f) of
(A, F) -> 1
(A, _) -> 2
(B, G) -> 3
(B, _) -> 4
(C, H) -> 5
(C, _) -> 6
-- pattern match on the function arguments
-- it doesn't need to be a tuple, but it's the easiest way to port to TS (I guess)
matchTyple' :: (A, F) -> Int
matchTyple' (A, F) = 1
matchTyple' (A, _) = 2
matchTyple' (B, G) = 3
matchTyple' (B, _) = 4
matchTyple' (C, H) = 5
matchTyple' (C, _) = 6
And to show a somehow more similar syntax to TS/JS, here's Reason version:
type a =
| A
| B
| C;
type f =
| F
| G
| H;
let matchType = (a, f) =>
switch((a, f)) {
| (A, F) => 1
| (A, _) => 2
| (B, G) => 3
| (B, _) => 4
| (C, H) => 5
| (C, _) => 6
};
Finding a way to match on tuples would indeed reduce a lot the boilerplate.
I'm not sure yet how to handle a few things regarding the implementation and API I have in mind, and also don't know which one is the best API.
Would be something like this
// `matchMany` or `matchTuple` ? or other ?
import { matchMany, catchAll as _ } from '@housinganywhere/match'
type A = 'A' | 'B' | 'C'
type F = 'F' | 'G' | 'H'
type ToMatch = [A, F] // would most of the time be defined in line
matchMany<ToMatch, number>(
[['A', 'F'], () => 1],
[['A', _], () => 2],
[['B', 'G'], () => 3],
[['B', _], () => 4],
[['C', 'H'], () => 5],
[['C', _], () => 6],
)('A', 'F') // 1
const isAF = matchMany<ToMatch, boolean>(
[['A', 'F'], () => true],
[[_, _], () => false],
)
isAF('A', 'F') // true
isAF('B', 'F') // false
matchMany
accepts a rest param, each one is a tuple of the ToMatch
tuple and the function that is going to be used in case of the match.
Is this a good API? Any ideas for something better?
What needs to be solved:
- Constrain the first type parameter to be a tuple
- From that type parameter be able to infer the rest of the types
Considerations:
- Now
match
is exhaustive, for this tuple match it's not possible (not that I know off with current state of TS). Should be handle the error or find a way to at least get one match?
- Examples show a two items tuple, it should work for any tuple. But we could maybe hardcode it for 2, 3 and 4 (or 5?) if it's easier than for all.
- How to type the
catchAll
, we could use any
but doesn't feel like good idea 😕