-
Notifications
You must be signed in to change notification settings - Fork 2
Core Library
import { match, against, when, otherwise, pluck, spread, cata } from 'match-iz'
- match(), against(), when(), otherwise()
- pluck() for extracting values from arrays/objects
- spread() for defining identical object-key patterns
- cata() for ADTs/monads
match(value)(...predicates)
// returns: winning value
against(...predicates)(value)
// returns: winning value
Each predicate receives the value
passed to match
(or against
, but we'll just talk about match
). The first one to return a truthy value wins, and match
returns it.
So you could just use match
like this:
match(haystack)(
haystack => undefined,
haystack => null,
haystack => 'hi'
)
// "hi"
However, using when
makes match
more powerful:
when(pattern)(handler)
// returns: value => { matched, value }
// Uncurried version:
when(pattern, handler)
// The uncurried version supports an arbitrary number of guards:
when(pattern, guard, handler)
when(pattern, guard1, guard2, guard3, handler)
// Guards work exactly like patterns, eg:
when(
isArray,
lastOf(isNumber),
x => x.length > 4,
() => {
return "it's an array of length >4 whose last element is a number"
}
)
handler
can be a function or a literal. pattern
is described by example throughout this page.
when
builds predicates that return objects like this:
{
matched: () => with pattern return true|false,
value: () => with handler return result
}
If match
sees such an object return from a predicate:
-
matched()
is run to determine the win-state -
value()
retrieves the winning value
So without when
, you could do this to emulate it:
match(haystack)(
haystack => ({
matched: () => haystack.length > 0,
value: () => 'haystack has length'
}),
haystack => ({
matched: () => !haystack,
value: () => 'haystack is falsy'
})
)
// The `when` equivalent of the above:
match(haystack)(
when(haystack => haystack.length > 0, 'haystack has length'),
when(haystack => !haystack, 'haystack is falsy')
)
otherwise(handler)
// returns: winning value
Always wins, so put it at the end to deal with fallbacks.
handler
can be a function or a literal.
You can use allOf
(AND) anyOf
(OR) and not
(NOT) to build more complex conditions:
match(haystack)(
when(allOf({ msg: endsWith('world!') }, { num: not(42) }), () => {
return 'A'
}),
when(anyOf({ msg: not(startsWith('hello')) }, { num: 42 }), () => {
return 'B'
}),
when(anyOf(1, 2, not('chili dogs')), () => {
return 'C'
})
)
Deprecated behaviour: Until version 3.0.0, OR could be achieved by using an array in your when
, so long as the haystack is not an array itself:
// OR example:
match({ message: 'hello wrrld!', number: 42 })(
when(
[
// if message ends with "world!" OR number === 42
{ message: endsWith('world!') },
{ number: 42 }
],
'ok!'
)
)
// "ok!"
This behaviour was deprecated in version 2.3.0 and removed in version 3.0.0 to prevent unusual behaviour when working with haystacks that may or may not be an array.
Use pluck
to extract values of interest when matching against array/object haystacks:
import { pluck } from 'match-iz'
match(action)(
when({ type: 'add-todo', payload: pluck() }, payload => {
return `Adding todo: ${payload}`
})
)
// `pluck` accepts patterns, so you
// can guard before extracting values:
match([1, 2, 3])(
when([1, 2, pluck(3)], three => {
return '[2] is the number 3!'
}),
when([1, 2, pluck(isNumber)], num => {
return `[2] is a number: ${num}`
})
)
The TC39 spec proposes both conditional and destructuring behaviour within the same syntax:
// checks that `error` is truthy, and destructures
// it for use in the winning block:
when ({ error }) { <Error error={error} />; }
Very concise! Unfortunately, we can't do that with current syntax.
But we can lean on Object initializer notation to get close:
import { defined } from 'match-iz'
// without:
when({ error: defined }, <Error {...props} />)
const error = defined
// with:
when({ error }, <Error {...props} />)
spread()
just makes it easy to do this with more than one prop:
const { loading, error, data } = spread(defined)
loading === defined // true
error === defined // true
data === defined // true
when({ loading }, <Loading />)
when({ error }, <Error {...props} />)
when({ data }, <Page {...props} />)
It uses Proxy to achieve this.
Use cata()
to integrate match-iz
with your ADTs/monads:
import { cata } from 'match-iz'
// Specify how match-iz can detect
// monads and extract their values
const { just, nothing } = cata({
just: m => m?.isJust,
nothing: m => m?.isNothing,
getValue: m => m?.valueOf()
})
match(maybeDate('2022-01-01'))(
just(dateObj => {
console.log('Parsed date: ', dateObj)
}),
nothing(() => {
console.log('Invalid date')
})
)
match-iz
🔥 | on npm | docs home | top of page