loading...
Cover image for Pattern-Match your JavaScript with Z

Pattern-Match your JavaScript with Z

kayis profile image K ・2 min read

Cover image by Dennis Skley on Flickr

Functional programming techniques seem to be rather popular these days. People are using monads in JavaScript, write compilers in OCaml and even hardcore object-oriented languages like Java now support closures.

Wouldn't it be nice if we could use some of the higher-level abstractions, like pattern matching, in omnipresent languages like JavaScript?

There is an TC39 proposal for new syntax that enables this feature, but it just stage-1 and didn't see updates for nine months.

Luckily for us, there is a library called Z that lets us pattern match without the need for additional syntax.

Why

Pattern matching is a switch on steroids; you can create powerful conditions such based on Object properties or Array content without manipulating the Object or Array itself. That amount of power leads you to write functional, immutable, and expressive code instead imperative, which reduces a lot of complexity and bugs.

What

Z is a JavaScript library that allows us to use pattern matching in JavaScript without the need for new syntax. We install an NPM package and are ready to go!

How

The Z package exports a matches function we can use like we would use if or switch.

One main difference is, matches returns the case that matched, so it behaves like an expression instead of a statement. If nothing matched, it will return undefined.

So, what can it do?

Like switch it can match by value.

const result = matches(x)(
  (c = 1) => "One!",
  (c = 2) => "Two!",
  (c) => "Something different..."
);

But, unlike switch, it can also match more complex values.

const result = matches(x)(
  (c = {data: 123}) => "We got data!",
  (c = {error: 404}) => "We got an error!",
  (c = [1,2,3]) => "We got an array!"
);

It's also possible to match by type.

const result = matches(o)(
  (c = Date) => "We got the date " + c.toString(),
  (c = Number) => "We got the number " + c,
  (c = Array) => "We got an array!"
);

We can also use it to destructure arrays or expect arrays with a specific length.

const result = matches(["hello", ",", "world", "!"])(
  (first, tail) => first + tail.join(""),
  (first, tail = []) => "Array only has one element!"
);

Conclusion

Z uses standard JavaScript syntax, but it plays some tricks here. Seemingly it compares our value with the default values of our callback functions. This decision leads to the unusual usage of the = operator instead of the == and === operators.

It also seems to be planned to make a strict matching mode, that throws an error instead of returning undefined if a check wasn't exhaustive.

While I don't know if this is the best way to implement pattern matching in JavaScript it's at least an interesting one and could help to prevent some errors while leading to more concise code.

Tell me what do you think in the comments :)


If you like my posts, you can also follow me on Twitter!

Posted on Oct 16 '17 by:

kayis profile

K

@kayis

Taking care of developer relations at Moesif and creating educational content at fllstck.dev

Discussion

markdown guide
 

This is incredible. I have a lot of questions regarding how this is done. I use Ramda's cond quite a bit. But I really like this syntax.

I'm curious to see how this works when minified. I believe it is using some type of reflection.

 

It seems to use a package called js-function-reflector which transforms a function object to it's string representation and parses it again.

github.com/arrizalamin/js-function...

 

and js-function-reflector uses the toString() method availaible on functions

the toString()

That doesn't seem like it would work for minified code as the parameter names would be uglified.

It works as long as you don't compile the default arguments to ES5.

 

One limitation I see is something like this is not possible:

import allPass from 'mojiscript/logic/allPass'
import cond from 'mojiscript/logic/cond'

const isFizz = num => num % 3 === 0
const isBuzz = num => num % 5 === 0
const isFizzBuzz = allPass ([ isFizz, isBuzz ])

const fizziness = cond ([
  [ isFizzBuzz, 'FizzBuzz' ],
  [ isFizz, 'Fizz' ],
  [ isBuzz, 'Buzz' ],
  [ () => true, x => x ]
])

fizziness(1) //=> 1
fizziness(3) //=> 'Fizz'
fizziness(5) //=> 'Buzz'
fizziness(15) //=> 'FizzBuzz'
 

Very interesting, I would use it mostly to do validation. Looking forward to see what it becomes in the future.

 

Yes, I think the type-checking is the most practical feature of it, if it's exhaustive it could prevent many typical JavaScript bugs :)

 

Yeah, in functional languages it usually replaces conditionals but javascript has a plethora of them. This is super cool though.

 

Absolutely agree, I use Joi but it still seems not "natural enough" for me, always thought Javascript diserved something better. Here you go :)

 
[deleted]
 

Glad, you like it :)

 

Life's filling up with more and more z and z and... Good catch, did not know about this lib :)