DEV Community

Cover image for Pattern matching = switch++
Conan
Conan

Posted on

Pattern matching = switch++

Photo by Parham Moieni on Unsplash

After learning about the exciting, but sadly only Stage 1 proposal for pattern-matching in JavaScript, I felt compelled to write a library that tries to, erm, match it as closely as I could:

import { match, when, otherwise } from 'match-iz'

match(haystack)(
  when(needle)(true),
  when(thread)(false),
  otherwise('bale')
)
Enter fullscreen mode Exit fullscreen mode

Essentially, pattern-matching is if/then and switch/case with a little more declarative cowbell. ๐Ÿฎ ๐Ÿ””

We can probe into an object:

const todosReducer = (state, action) =>
  match(action)(
    when({ type: 'set-vis-filter' })
      (({ visFilter }) => ({
        ...state,
        visFilter
      })),

    when({ type: 'add-todo' })
      (({ text }) => ({
        ...state,
        todos: [...state.todos, { text, completed: false }]
      })),

    otherwise(state)
  )
Enter fullscreen mode Exit fullscreen mode

...test against predicates as well as literals:

const defined = obj => !!obj

function AccountPage(props) {
  return match(props)(
    when({ error: true })(<Error {...props} />),
    when({ loading: true })(<Loading />),
    when({ data: defined })(<Page {...props} />),
    otherwise(<Logout />)
  )
}
Enter fullscreen mode Exit fullscreen mode

...which is handy for dealing with server-responses:

const getJsonLength = async () =>
  match(await fetch('/json'))(
    when({ status: 200 })
      (({ headers: { 'Content-Length': s } = {} }) => {
        return `size is ${s}`
      }),

    when(({ status }) => status >= 500)('Server error!'),
    when({ status: 404 })('JSON not found'),
    when({ status: gte(400) })('Flagrant error!'),

    otherwise("I didn't understand that...")
  )
Enter fullscreen mode Exit fullscreen mode

...and match strings against regular-expressions, passing the result into the handler:

match('1 + 2')(
  when(/(?<left>\d+) \+ (?<right>\d+)/)
    (({ groups: { left, right } }) =>
      add(left, right)
    ),

  when(/(?<left>\d+) \- (?<right>\d+)/)
    (({ groups: { left, right } }) =>
      subtract(left, right)
    ),

  otherwise("I couldn't parse that!")
)
// 3
Enter fullscreen mode Exit fullscreen mode

The only thing we can't have is else instead of otherwise. ๐Ÿ˜

The full TC39 proposal is more far-reaching because it is inspired by the advanced pattern-matching features offered by functional languages with mature implementations.

Still, while we wait for the language feature I think match-iz (along with many similar libraries) is a nice tool for constructing some of the conditional logic we use in our JavaScript.

It's not a substitute for well named intermediate variables of course, but I hope you can see its utility.

Discussion (0)