Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Haskell in ES6: Part 1 #1

Open
mateogianolio opened this issue Feb 14, 2016 · 2 comments
Open

Haskell in ES6: Part 1 #1

mateogianolio opened this issue Feb 14, 2016 · 2 comments

Comments

@mateogianolio
Copy link
Member

mateogianolio commented Feb 14, 2016

Originally posted 2015-11-12.

This post is the first in a series that will be dedicated to implementating native versions of Haskell functions according to JavaScript ES6 standards. Full source can be found in this GitHub repo. You are more than welcome to contribute!

ƒ.comp

Function composition.

(.) :: (b -> c) -> (a -> b) -> a -> c
/**
 * Function composition
 * @param ...fs functions to compose
 * @return composed function
 **/
export function comp (...fs) {
  return (v, ...args) =>
    fs.reduceRight(
      (g, f) => f(g, ...args), v
    );
}

Examples

var add = x => x + x,
    pow = x => x * x,
    inv = x => 1 / x;

var comp = ƒ.comp(add, pow, inv);

comp(1); // => 2
/**
 * Explained:
 * 1) inv 1 / 1 => 1
 * 2) pow 1 * 1 => 1
 * 3) add 1 + 1 => 2
 **/

comp(4); // => 1/8

ƒ.flip

flip f takes its (first) two arguments in the reverse order of f.

flip :: (a -> b -> c) -> b -> a -> c
/**
 * Flip function arguments
 * @param f function to flip
 * @return f applied with args in reverse order
 **/
export function flip (f) {
  return (a, b, ...args) =>
    f(b, a, ...args);
}

Examples

var add = (a, b) => a / b,
    three = (a, b, c) => [a, b, c],
    flip = ƒ.flip(add);

flip(10, 5); // => 1/2
flip(1, 10); // => 10

flip = ƒ.flip(three);
flip(1, 2, 3); // => [2, 1, 3]

ƒ.until

until p f yields the result of applying f until p holds.

until :: (a -> Bool) -> (a -> a) -> a -> a
/**
 * Applies a function which is passed as the second argument to
 * the third argument and it comapares the result with the condition,
 * if the condition evaluates to true, it prints the result, if not,
 * it passes the result to the function and repeats the cycle as long
 * as the condition is matched
 * @param condition condition to be applied to f
 * @param f function to match against
 * @return result if condition is true else repeat cycle
 **/
export function until (condition, f) {
  return (...args) => {
    var r = f(...args);
    return condition(r) ? r : until(condition, f)(r);
  };
}

Examples

var condition = x => x > 100,
    inc = x => x + 1,
    until = ƒ.until(condition, inc);

until(0); // => 101

condition = x => x === 5;
until = ƒ.until(condition, inc);

until(3); // => 5

List operations

head extracts the first element of a list, which must be non-empty.

last extracts the last element of a list, which must be finite and non-empty.

tail extracts the elements after the head of a list, which must be non-empty.

init returns all the elements of a list except the last one. The list must be non-empty.

head :: [a] -> a
last :: [a] -> a
tail :: [a] -> [a]
init :: [a] -> [a]
export function head (xs) { return xs[0]; }
export function last (xs) { return xs[xs.length - 1]; }
export function tail (xs) { return xs.slice(1); }
export function init (xs) { return xs.slice(0, -1); }

Examples

ƒ.head([5, 27, 3, 1]); // => 5
ƒ.last([5, 27, 3, 1]); // => 1
ƒ.tail([5, 27, 3, 1]); // => [27, 3, 1]
ƒ.init([5, 27, 3, 1]); // => [5, 27, 3]

Special folds

concat yields the concatenation of all the elements of a container of lists.

concatMap maps a function over all the elements of a container and concatenate the resulting lists.

concat :: Foldable t => t [a] -> [a]
concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
export function concat (...xs) {
  return xs.reduce(
    (a, b) => a.concat(b)
  );
}

export function concatMap (f, ...xs) {
  return concat(xs.map(f));
}

Examples

ƒ.concat([5], [27], [3]); // => [5, 27, 3]
ƒ.concatMap(x => 'hi ' + x, 1, [[2]], 3); // => ['hi 1', 'hi 2', 'hi 3']

ƒ.zip and ƒ.zipWith

zip takes two lists and returns a list of corresponding pairs. If one input list is short, excess elements of the longer list are discarded."

zipWith generalises zip by zipping with the function given as the first argument, instead of a tupling function. For example, zipWith (+) is applied to two lists to produce the list of corresponding sums."

zip :: [a] -> [b] -> [(a, b)]
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
/**
 * Zip two arrays into a list of n-ples
 * @param ...xs arrays to zip
 * @return a list of of n-ples
 **/
export function zip (...xs) {
  var r = [],
      nple = [],
      length = Math.min(...xs.map(x => x.length));

  for (var i = 0; i < length; i++) {
    xs.forEach(
      x => nple.push(x[i])
    );

    r.push(nple);
    nple = [];
  }

  return r;
}

/**
 * Generalises zip by zipping with the function given
 * as the first argument, instead of a tupling function.
 * @param op function to zip with
 * @param ...xs arrays to zip
 * @return array zipped with the op function
 **/
export function zipWith (op, ...xs) {
  zip(...xs).map(
    (x) => x.reduce(op)
  );
}

Examples

var a = [0, 1, 2],
    b = [3, 4, 5],
    c = [6, 7, 8];

ƒ.zip(a, b); // => [[0, 3], [1, 4], [2, 5]]
ƒ.zipWith((a, b) => a + b, a, b, c); // => [9, 12, 15]
@zheeeng
Copy link

zheeeng commented Oct 10, 2017

mistake in zip function:

export function zip (...xs) {
  var r = [],
      nple = [],
      // should be: length = Math.min(...xs.map(x => x.length))
      // or: length = Math.min.call(null, ...xs.map(x => x.length))
      length = Math.min(null, ...xs.map(x => x.length));

  for (var i = 0; i < length; i++) {
    xs.forEach(
      x => nple.push(x[i])
    );

    r.push(nple);
    nple = [];
  }

  return r;
}

@mateogianolio
Copy link
Member Author

Thanks, fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants