You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In this article we will delve into the depths of JavaScript lambda calculus and how it can help us increase both productivity and code brevity.
λ
Lambda calculus (also written as λ-calculus) is a formal system in mathematical logic for expressing computation based on function abstraction and application using variable binding and substitution.
The first requisite of lambda calculus is anonymous functions. JavaScript has featured these as long as I can remember.
function(){return'I am anonymous.';}varf=function(x){// anonymous function bound to a variable freturnx*2;}// anonymous functions can be mapped to an array[1,2,3].map(function(x){returnx*2;});[1,2,3].map(f);
Anonymous functions can also be (and is extensively) used as closures.
The second simplification that lambda calculus offers is currying, which can be accomplished in JavaScript (see e.g. currying vs partial application). I won't go into detail, because at the time of writing I don't find currying very useful in JavaScript.
Arrow function
A big – well, at least in terms of brevity – problem with anonymous functions: It was necessary to type both function and return, which lead to space issues when passing the functions as function arguments.
Enter the arrow function. Mozilla Developer Network, the #1 JavaScript resource, has a great code snippet of the arrow function's syntax:
// Basic syntax:(param1,param2,paramN)=>{statements}(param1,param2,paramN)=>expression// equivalent to: => { return expression; }// Parentheses are optional when there's only one argument:(singleParam)=>{ statements }singleParam=>{statements}// A function with no arguments requires parentheses:()=>{ statements }// Advanced:// Parenthesize the body to return an object literal expression:params=>({foo: bar})// Rest parameters are supported(param1,param2, ...rest)=>{ statements }
Compare function (x) { return x; } (21 characters, not counting spaces) with x => x (4 characters).
The code
Long introduction? Let's get down to business. We'll build a wrapper to be able to pass arrow functions as arguments. To do this we need some knowledge about the function prototype.
We note that Function.prototype.toString() returns the source code of a function instance. I'll write the wrapper around nBLAS, a Node.js C++ binding I wrote for BLAS (Basic Linear Algebra Subsystems), but you can pick any library you want.
The aim with the wrapper is being able to use the following syntax:
// export is to be able to resolve variable names to variablesvara=exports.a=newFloat64Array([1,2]);varb=exports.b=newFloat64Array([3,4]);// additionλ((a,b)=>a+=b);// a := [4, 6]λ((a,b)=>a+b);// [4, 6]// dot productλ((a,b)=>a*b);// 36// scalingλ(a=>a*=5);// a:= [20, 30]λ(a=>a*5);// [20, 30]
I have only implemented the above functionality for demonstrative purposes.
varnblas=require('nblas');functionλ(f){// (a, b) => a + b to ['a,b', 'a+b']varargs=f.toString().replace(/[ \(\)]/g,'').split('=>');// get vars from 'a,b', switch 'export' to 'window' for browservarvars=args.shift().split(',').map(arg=>exports[arg]).reverse();args=args.join('');vartmp;// hook behaviourif(args.indexOf('+=')!==-1){returnnblas.axpy(...vars);}elseif(args.indexOf('+')!==-1){tmp=newvars[0].constructor(vars[1]);nblas.axpy(vars[1],tmp);returntmp;}elseif(args.indexOf('*')!==-1){if(vars.length===2)returnnblas.dot(...vars);if(args.indexOf('=')!==-1)returnnblas.scal(...args.split('*='));tmp=newvars[0].constructor(vars[0]);nblas.scal(tmp,Number(args.split('*')[1]));returntmp;}elsereturnf(...vars);// fallback to executing supplied function if no hook found}
There you go, (kind of) operator overloading in JavaScript! To use the spread operator (...) you'll need to use babel or run the script with node --harmony. Otherwise you can switch f(...x) to f.apply(null, x).
Merry christmas and happy hacking.
The text was updated successfully, but these errors were encountered:
Originally posted
2015-12-18
.In this article we will delve into the depths of JavaScript lambda calculus and how it can help us increase both productivity and code brevity.
λ
The first requisite of lambda calculus is anonymous functions. JavaScript has featured these as long as I can remember.
Anonymous functions can also be (and is extensively) used as closures.
The second simplification that lambda calculus offers is currying, which can be accomplished in JavaScript (see e.g. currying vs partial application). I won't go into detail, because at the time of writing I don't find currying very useful in JavaScript.
Arrow function
A big – well, at least in terms of brevity – problem with anonymous functions: It was necessary to type both
function
andreturn
, which lead to space issues when passing the functions as function arguments.Enter the arrow function. Mozilla Developer Network, the #1 JavaScript resource, has a great code snippet of the arrow function's syntax:
Compare
function (x) { return x; }
(21 characters, not counting spaces) withx => x
(4 characters).The code
Long introduction? Let's get down to business. We'll build a wrapper to be able to pass arrow functions as arguments. To do this we need some knowledge about the function prototype.
We note that
Function.prototype.toString()
returns the source code of a function instance. I'll write the wrapper around nBLAS, a Node.js C++ binding I wrote for BLAS (Basic Linear Algebra Subsystems), but you can pick any library you want.The aim with the wrapper is being able to use the following syntax:
I have only implemented the above functionality for demonstrative purposes.
There you go, (kind of) operator overloading in JavaScript! To use the spread operator (
...
) you'll need to use babel or run the script withnode --harmony
. Otherwise you can switchf(...x)
tof.apply(null, x)
.Merry christmas and happy hacking.
The text was updated successfully, but these errors were encountered: