GithubHelp home page GithubHelp logo

functorlawswithjsverify's Introduction

README

This is the source for a blog post that is at https://www.lunatech.com/blog/W7IorBUAAMac74Ew/functional-abstractions-in-javascript-check-functors-laws going to appear soon on Lunatech’s blog.

To run the software you need to have:

  1. Node.js
  2. npm i jsverify

Have fun!

Functional abstractions in JavaScript: check functors laws

JavaScript is a curious mix of programming paradigms. In this post we will focus on the functional traits of JavaScript (JS). In particular we will see how to check that a function satisfies the Functor’s laws.

A Functor is a fundamental abstraction for functional programmers. It behaves like a (very limited) container: you can interact with it only by altering its content.

In the functional community this interface is called map. In the context of Functors, this interface takes the name of fmap.

Basic JS arrays offer a map function:

var x = [1, 2, 3]
console.log("Mapping +1: ", x.map((x) => x+1))
console.log("The original array: ", x)

In the following we are going to check if map respects the Functor laws; in other words: is the object array a Functor?

First let’s define a curried fmap to make laws definitions look nice:

const fmap = (fn) => (
    (array) => array.map(fn))

This works as follows:

<<fmap>>
console.log(fmap((x)=> x+1)([1,2,3]))

Then let’s define the two laws that must hold to call something a Functor.

The first law says that mapping the identity function on a Functor should produce the same Functor.

The identity function is easy to define:

const id = (x) => x

The first law then becomes:

fmap(id) = id

The second law says that mapping the composition of two functions on a functor should produce the same Functor that is obtained by composing the fmap.

The compose function can be defined as:

const compose = (fn1,fn2) => (
    (arg) => fn2(fn1(arg)))

The second law then becomes:

fmap(compose(f,g)) = compose(fmap(f),fmap(g))

So far we have defined the Functor laws. Now we want to check the JS arrays satisfy them.

The Haskell community donated a great tool to test properties of functional software: QuickCheck. We will use the JS implementation of it: jsverify.

In jsverify we can define software properties like the following (from jsverify’s README):

var jsc = require("jsverify");

// forall (f : bool -> bool) (b : bool), f (f (f b)) = f(b).
var boolFnAppliedThrice =
  jsc.forall("bool -> bool", "bool", function (f, b) {
    return f(f(f(b))) === f(b);
  });

console.log(jsc.check(boolFnAppliedThrice));

As you can see, jsverify generates tests that validate if the property holds. By default it generates 100 tests per property.

Let’s then define our laws as jsverify properties.

For this we will need a custom equals function as JS behaves in a peculiar way when comparing empty arrays:

console.log([] === [])

So our equals will be:

const equals = (x,y) => (JSON.stringify(x) === JSON.stringify(y))

Which behaves as we wish for empty arrays:

<<equals>>
console.log(equals([],[]))

Now we refine the laws in jsverify format:

<<ids>>
<<compose>>
<<fmap>>

var jsc = require("jsverify");

var arrayInt = jsc.array(jsc.integer());

const fmap_law1 = jsc.forall(
    arrayInt, 
    (x) =>
        //fmap(id) == id
        equals(
            fmap(id)(x),
            id(x))
);

const fmap_law2 = jsc.forall(
    "integer -> integer", 
    "integer -> integer", 
    arrayInt, 
    (f,g,x) =>
        //fmap(compose(f,g)) = compose(fmap(f),fmap(g))
        equals(
            fmap(compose(f,g))(x),
            compose(fmap(f),fmap(g))(x)
        ));

Note that here we are applying the curried functions with the input generated by jsverify (i.e., x).

And at last we can run check our laws hold for an array of integers:

<<equals>>

<<laws>>

console.log("First functor law satisfied?", jsc.check(fmap_law1));

console.log("Second functor law satisfied?", jsc.check(fmap_law2));

So JS arrays we can be rather confident that JS arrays are Functors!

This same approach can be applied to other functional abstractions, but this will be matter of later posts.

Hopefully this example will encourage you to learn more abou Functors and to start using jsverify.

Good hacking!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.