hu

Functional-oriented utility helper library

hu

Stagebeta
Funnytrue

hu is a functional-oriented utility helper library inspired by haskell's prelude and lodash/underscore

Unlike other well-known libraries such as Lodash or Sugar.js, hu only provides a reduced but generic set of most common and useful functions. It aims to be a lightweight and small library which can be easily embedded as a part of an application, library or framework without making noise

hu library works well in ES5 compliant engines. Most of its functions are curried

  • Complete type checking helpers
  • Array and objects processors and transformers
  • Functional-oriented helpers (curry, partial, compose, memoize...)
  • String manipulation helpers
  • Equality comparison functions
  • Runs in node and browsers
  • Well tested with 100% of coverage
$ npm install hu --save

Via Bower package manager

$ bower install hu

Or loading the script remotely (just for testing or development)

<script src="//rawgithub.com/h2non/hu/master/hu.js"></script>

It works properly in any ES5 compliant environment, however just for a formalism, those environments are:

  • Node.js
  • Chrome >= 5
  • Firefox >= 3
  • Safari >= 5
  • Opera >= 12
  • IE >= 9

Functional programming tendency has grown (again) in the latest years. This paradigm, unlike others that may be more totalitarian and extended, forces a radical re-thinking in the way that programmers design and implement software

Functional programming thinking and conversion is not easy to apply, but it's really a quite attractive and funny paradigm, which helps to solve the same problems in a theoretically and conceptually clean (and sometimes more efficiently) way

A good approach to learn and apply functional programming principles is creating a functional-oriented library that assists by itself to do functional programming

In fact, hu was created to define a real scenario to do funny experiments with some pure functional programming principles

It's completely written in Wisp, a homoiconic Clojure-like language, which compiles into plain JavaScript that supports s-expressions and macros, allowing to extend the compiler features with the user’s own code

JavaScript is an ubiquitous, well-extended, multi-purpose and multi-paradigm cool language with which you can do a lot of funny things

Yes, I know, JavaScript is not a pure functional language, however its natural extensibility and meta-programming features allow to apply different paradigms to it and today there are a lot of languages that transpile into JavaScript that help providing a powerful syntax sugar and more features, like in this case using Wisp

hu is implemented keeping in mind the following “ambitious” functional focused goals:

  • Assume it's a first-class function only language
  • Pure functions as a norm
  • Immutability (when it's possible)
  • Avoid assignment, remove side-effects (when it's possible)
  • Tend to recursion instead of loops
  • Tend to high-order functions
  • Tend to functional composition
  • Tend to continuation-passing style
  • Exploit subroutines (like tail recursion call)
  • Exploit memorization (currying, partial, caching...)
  • Exploit combinators
  • Macros are a great thing, use&love it

JavaScript Harmony (ES6)

var { log, filter, even, inc } = require('hu')
 
log(map(filter({a: 1, b: 2}, even), inc))
// → { b: 3 } 

Or with the funny LiveScript

(a: 1, b: 2)
  |> filter _, even
  |> map _, inc
  |> log

Return: boolean | Alias: isBoolean

Checks if the given value is a boolean type

Return: boolean

Checks if the given value is a number type

Return: boolean

Checks if the given value is a finite number, or it can be coerced into it

Return: boolean

Is it NaN (not a number)? More accurate than the native isNaN function

Return: boolean

Checks if the given value is a string type

Return: boolean

Checks if the given value is a symbol type

Return: boolean | Alias: isFn

Checks if the given value is a function type

Return: boolean

Checks if the given value is a date type

Return: boolean | Alias: isPattern

Checks if the given value is a regexp type

Return: boolean

Checks if the given value is an array type

Return: boolean

Checks if the given value is an object type

Return: boolean

Checks if the given value is a native object type (it was createdd by the Object native constructor)

Return: boolean

Checks if the given value is an error type

Return: boolean

Checks if the given value is a DOM element object instance

Return: boolean | Alias: isArguments

Checks if the given value is an arguments object

Return: boolean | Alias: isUndefined

Checks if the given value is a undefined, void o null type

Return: boolean

Checks if the given value is a null type

Return: boolean

Checks if the given value is empty. Arrays, strings, or arguments objects with a length of 0 and objects with no own enumerable properties are considered empty values

Return: boolean | Alias: isNotEmpty

Checks if the given value is not empty

Return: boolean

Checks if the given value is a mutable data type. Objects, arrays, date objects, arguments objects and functions are considered mutable data types

Return: boolean

Checks if the given value is a primitive value type. Strings, numbers, booleans, symbols and null are considered primitives values

Return: boolean | Alias: canIterate

Checks if the given value can be iterated. Objects, arrays, and arguments objects are considered iterables data types

Return: string

Extract characters from the given string

Return: array

Split the given string by end of line tokens

Return: string

Join the given array into a string separated by end line token

Return: array

Returns an array of words (spaces separated)

Return: string

Join words of the given array into a string spaces separated

Return: array

Return an array of characters of the given string

Return: string

Join the strings of the given array

Return: string

Return the given unicode number into his equivalent character

Return: string

Reverse characters of the given string

Return: string

Repeat the given string

Return: string

Converts the characters &, <, >, ", and ' in the given string to their corresponding HTML entities

Return: boolean | Alias: isOdd

Returns true if the given number is odd

Return: boolean | Alias: isEven

Returns true if the given number is even

Return: boolean | Alias: isLower | Curried: true

Returns true if x it's lower than y

Return: boolean | Alias: isHigher | Curried: true

Returns true if x it's lower than y

Return: number

Returns the number with the highest value

Return: number

Returns the number with the lower value

Return: number

Increment the given value

Return: number

Decrement the given value

Return: number

Takes a number and returns either -1, 0, or 1 depending on the sign of the number

Return: boolean

Returns true if the given number is negative

Return: number

The negation of the given number

Return: number

One over the number: ie 1 / x

Return: number

Division truncated down toward negative infinity

Return: number

Returns the largest of zero or more numbers

Return: number

Returns the smallest of zero or more numbers

Return: number

Returns the absolute value of a number

Return: number

Returns the value of a number rounded to the nearest integer

Return: number

Returns a pseudo-random number between 0 and 1

Return: number

Returns the largest integer less than or equal to a number

Return: number

Returns the sine of a number

Return: number

Returns the tangent of a number

Return: number

Returns the cosine of a number

Return: number

Returns the arcsine of a number

Return: number

Returns the arctangent of a number

Return: number

Returns the cosine of a number

Return: number

Returns the smallest integer greater than or equal to a number

Return: number

Returns Ex, where x is the argument, and E is Euler's constant (2.718...), the base of the natural logarithm

Return: number

Returns the positive square root of a number

Type: number

Ratio of the circumference of a circle to its diameter, approximately 3.14159

Return: boolean | Curried: true

Checks if an element exists in the given array

Return: mixed | Alias: first

First item of the given array

hu.head([1, 2, 3]) // → 1 

Return: array | Alias: rest

Everything but the first item of the list

hu.tail([1, 2, 3]) // → [2, 3] 

Return: mixed | Alias: end

The last item of the list

hu.last([1, 2, 3]) // → 3 

Return: array

Everything but the last item of the list

hu.initial([1, 2, 3]) // → [1, 2] 

Return: boolean

Checks if the specified property name exists as a own property of the given object

hu.has({a: true}, 'a') // → true 

Return: array

Returns a sequence of the map's keys

hu.keys({a: true}) // → ['a'] 

Return: array

Returns a sequence of the map's values

hu.vals({a: true}) // → [true] 

Return: array | Alias: pairs

Returns a two dimensional array of an object’s key-value pairs

hu.keyValues({a: true}) // → [['a', true]] 

Return: array

Creates an object of given arguments. Odd indexed arguments are used for keys and evens for values

hu.toObject('a', true) // → {a: true} 

Return: object | Alias: assign

Assigns own enumerable properties of source object(s) to the destination object

hu.extend({x: true}, {y: false}) // → {x: true, y: false} 

Return: object

Adds function properties of a source object to the destination object

var methods = {
  somethingfunction () {
    // cool stuff 
  }
}
hu.mixin({x: true}, methods)
// → {x: true, something: function () {}} 

Return: object | Alias: mapValues | Curried: true

Maps object values by applying with the value return of each callback call on each one

function mapper(val) {
  return val * 2
}
hu.map({x: 2}, mapper) // → {x: 4} 

Return: object | Alias: filterValues | Curried: true

Iterates over properties of an object, returning an filtered new object of all elements where the callback returns true

function filter(val) {
  return val > 1
}
hu.map({x: 1, y: 2}, filter) // → {y: 2} 

Return: object

Creates a clone of the given object

var obj = {x: 1}
var newObj = hu.clone(obj)
newObj === obj // → false 

Return: object

Similar to extend, it returns an object that consists of the rest of the maps conj-ed onto the first

If a key occurs in more than one map, the mapping from the latter (left-to-right) will be the mapping in the result

var obj1 = {x: {y: {z: 2}}}
var obj2 = {x: {y: {a: 1}}}
var newObj = hu.merge(obj1, obj2)
// → {x: {y: {z: 2, a: 1}}} 

Return: object | Alias: forEach

Iterates over elements of an iterable object, executing the callback for each element. It will return the same given object

hu.each([1, 2], function (n) {
  console.log('Value:', n)
})

Return: number

Gets the size of the given collection

hu.size({x: 1, y: 2}) // → 2 

Return: object|array | Alias: clean

Returns a new collection which contains only the not empty values

hu.compact([1, null, , "", 5])
// → [1, 5] 

Return: function | Alias: identity

Returns a function that returns the given value

var getter = hu.constant('john')
getter() === 'john' // → true 

Return: mixed

Invokes a function binding itself function object context with the given arguments as array

function myFn(xy) { return x * y }
hu.apply(myFn, [2, 2]) // → 4 

Return: function

Creates a function that, when called, invokes the function with the this binding of thisArg and prepends any additional bind arguments to those provided to the bound function

function func(greeting) {
  return greeting + ' ' + this.name
}
func = hu.bind(func, { 'name': 'john' }, 'hi')
func() // → 'hi john' 

Return: function

Creates a function that, when called, invokes func with any additional partial arguments prepended to those provided to the new function

function greet(greetingname) {
  return greeting + ' ' + name;
}
var hi = hu.partial(greet, 'hi');
hi('john') // → 'hi john' 

Return: function

Creates a function which accepts one or more arguments of the given function that when invoked either executes the function returning its result

var curried = hu.curry(function(abc) {
  console.log(+ b + c)
})
curried(1)(2)(3) // → 6 
curried(1, 2)(3) // → 6 
curried(1, 2, 3) // → 6 

Return: function

Creates a function that is the composition of the provided functions, where each function consumes the return value of the function that follows

function name(name) {
  return name.toLowerCase() + '!'
}
function greet(name) {
  return 'Hi ' + name
}
var welcome = hu.compose(name, greet);
welcome('John') // → 'Hi john!' 

Return: function

Creates a function that memoizes the result of the the given function. If resolver is provided it will be used to determine the cache key for storing the result based on the arguments provided to the memoized function. The resolver function just uses the first argument to the memoized function as the key

var multiply = hu.memoize(function (n) {
  return n * 2
})
multiply(2) // → 4 (computed value) 
multiply(2) // → 4 (memoized value) 

With custom resolver function to define memoized values

var multiply = hu.memoize(function (n) {
  return n * 2
}, function (n) {
  return n === 2 ? n + 1 : n
})
multiply(1) // → 2 (computed value) 
multiply(2) // → 4 (computed value) 
multiply(3) // → 4 (memoized value, from 2 value) 

Return: function | Curried: true

Creates a function that provides value to the wrapper function as its first argument. Additional arguments provided to the function are appended to those provided to the wrapper function

function hello(name) {
  return "hi " + name;
}
hello = hu.wrap(hello, function (fntexttype) {
  return "before, " + fn("moe") + "" + text + "" + type + ", after"
}, "type")
hello("salutation") // → 'before, hi moe, type: salutation, after' 

Return: function

Creates a function that is restricted to execute function once time. Subsuquents calls to the function will return the memoized value of the initial call

var times = 0
var init = hu.once(function () {
  return times += 1
})
init() // → 1 
init() // → 1 

Return: function | Curried: true

Creates a function that, when executed, will only call the fn function at most once per every wait milliseconds

var test = hu.throttle(function () {
  console.log(Date.now())
}, 100)
test() // → first call 
test() // → no call 
setTimeout(test, 150) // → second call 

Return: function | Curried: true

Creates a function that is restricted to be executed a finite number of times. Subsuquents calls to the function will return the memoized value of the latest call

var times = 0
var init = hu.times(function () {
  return times += 1
}, 2)
init(); // → 1 
init(); // → 2 
init(); // → 2 

Return: void

Executes the given function after wait milliseconds. You can provide arguments that will be passed to the function when it's invoked

function delayed(text) {
  console.log(text)
}
hu.defer(delayed, 1000, 'later')
// → logs 'later' after one second 

Return: function

Return a function that executes the given function after wait milliseconds when it's called. You can provide arguments that will be passed to the function when it will be invoked

function delayed(textname) {
  console.log(text, name)
}
var lazy = hu.debounce(delayed, 1000, 'later')
lazy('call') // → logs 'later call' after one second 

Return: boolean | Alias: equal, deepEqual, 'isDeepEqual'

Compares primitives types and data objects in a type-independent manner. Clojure's immutable data structures define -equiv (and thus =) as a value, not an identity, comparison.

Return: boolean | Alias: isRegExpEqual, patternEqual | Curried: true

Check if the given dates are equal

Return: boolean | Alias: dateEqual | Curried: true

Check if the given dates are equal

Return: boolean | Alias: arrayEqual | Curried: true

Check if the given arrays has the same elements

Return: boolean | Alias: objectEqual | Curried: true

Checks if the given objects values and keys are equals

Return:

Write the given arguments in the console

Type: boolean

Checks if the current runtime JavaScript environment is in a browser context

Return: void

The no-operation function, that returns void

Return: number

Returns an integer timestamp for the current time

Return: object

Environment specific global object

Wanna help? Cool! It will be really apreciated :)

You must add new test cases for any new feature or refactor you do, always following the same design/code patterns that already exist

Tests specs are completely written in Wisp language. Take a look to the language documentation if you are new with it. You should follow the Wisp language coding conventions

Only node.js is required for development

Clone/fork this repository

$ git clone https://github.com/h2non/hu.git && cd hu

Install package dependencies

$ npm install

Compile code

$ make compile

Run tests

$ make test

Browser sources bundle generation

$ make browser

Release a new version

$ make release

Copyright (c) Tomas Aparicio

Released under the MIT license