0.1.0 • Published 2 years ago

fun-lib v0.1.0

Weekly downloads
2
License
-
Repository
-
Last release
2 years ago

fun-lib Build Status

fun-lib is short of Functional Library - a JavaScript library that attempts to address two things missing in modern JavaScript to make it more attractive for functional programmers - lazy sequences and immutable data structures. It is also short for fun library in a sense that I'm having great fun implementing it.

Rationale

There are many fantastic JavaScript libraries that provide plenty of good things for functional programmer's heart content, yet none of them fully utilizes all those amazing features brought us by ES6. I believe that ES6 is already mostly supported in every modern/sane browser as well as in the current Node.js releases and there is no need to transpile it.

So I decided to implement a) lazy sequences (streams) built on the generator/iterator interface; b) a sequence library that works with virtually anything that supports iterator interface (i.e provides [Symbol.iterator] method) and on top of that c) immutable data structures, such as vectors, maps, and sets.

Ideally, a), b), and c) should be as less coupled as possible and be usable separately.

API

The library's API is inspired by Clojure's sequence library and some other languages, but strives to be as succinct yet practicle as possible.

Usage

const {map, reduce, concat} = require("fun-lib/lib/Seq")

const reducer = (acc, x) => acc + x
const mapper = x => x + 1
let seq = concat([1, 2, 3], [4], [5, 6])
let result = reduce(reducer, 0, map(mapper, seq))
// result is (1+1)+(2+1)+(3+1)+(4+1)+(5+1)+(6+1) = 27

One thing that makes the library stand out is that sequence functions work with anything supporting iterator interface. That is why the following code is perfectly legal:

// Generator for infinite sequence of Fibonacci numbers
const fib = function *() {
      let a = 1, b = 1
      while (true) {
            let c = a + b
            yield a
            a = b
            b = c
      }
}

// sum of the first 10 numbers
const reducer = (acc, x) => acc + x
let sum = reduce(reducer, take(10, fib())) // sum = 143

Most of the sequence library performs "lazily"

let counter = 0
const mult2 = x => {
    counter++
    return x*2
}
let seq = map(mult2, [1, 2, 3]))
console.log(counter) // prints 0
// compare with Array's map
let mappedArray = [1, 2, 3].map(mult2)
console.log(counter) // prints 3

To force the sequence it should be either consumed one by one using first() and rest() functions or to iterate over it, e.g. using for( .. of seq) loop.

Interfaces

Pre-ES6 JavaScript provided only duck-typing, that is a presense of a certain named property is considered to be enough to say that an object supports some interface.

if (object.quack)
   console.log("The object is certainly a duck because it quacks")

ES6 introduced a new basic type - Symbol. It is different from any other type and has some unique features that can be used to build more robust interfaces. Namely, it is guaranteed that a symbol once created is unique. What it gives? Imagine that two distinct libraries have defined a symbol Nil

// inside library A
exports.Nil = Symbol("Nil")

// library B
exports.Nil = Symbol("Nil")

const a = require("lib-a")
const b = require("lib-b")
console.log(a.Nil == b.Nil) // prints false

In pre-ES6 code the following scenario is possible

exports.Nil = null // library A
exports.Nil = null // library B
console.log(a.Nil = b.Nil) // prints true

More over, as Symbol is a distinct basic type, it's value different from values of any other types and can be effectively used as a no-value.

With the following in mind, fun-lib utilizes symbols for the purpose of defining interfaces.

Currently lib/Interfaces defines only two interfaces: iterator which is just an alias for standard [Symbol.iterator] and size. There are also some handy methods that check for presense and query an object for an interface.

const I = require("lib/Interfaces")
if (I.isIterable(someObject)) {
  let it = I.getIterator(someObject)
  doSomethingWithIterator(it)
}
if (I.isMeasurable(someSequence))
  console.log("someSequence size is", I.getSize(someSequence))

In the future I think it should be used to provide more efficient, native sequence operations used as a short-cut in some sequence methods.

0.1.0

2 years ago

0.0.4

8 years ago

0.0.3

8 years ago

0.0.2

8 years ago

0.0.1

8 years ago