Living Document. J. S. Choi, 2018-12.
The WHATWG Fetch Standard contains several examples of using the DOM fetch
function, resolving its promises into values, then processing the values in
various ways. These examples may become more easily readable with smart pipelines.
With smart pipelines | Status quo |
---|---|
'/music/pk/altes-kamuffel'
|> await fetch(#)
|> await #.blob()
|> playBlob; |
fetch('/music/pk/altes-kamuffel')
.then(res => res.blob())
.then(playBlob); |
[Equivalent to above.] |
playBlob(
await (
await fetch('/music/pk/altes-kamuffel')
).blob()
); |
'https://example.com/'
|> await fetch(#, { method: 'HEAD' })
|> #.headers.get('content-type')
|> console.log; |
fetch('https://example.com/',
{ method: 'HEAD' }
).then(response =>
console.log(
response.headers.get('content-type'))
); |
'https://example.com/'
|> await fetch(#, { method: 'HEAD' })
|> #.headers.get('content-type')
|> console.log; |
console.log(
(await
fetch('https://example.com/',
{ method: 'HEAD' }
)
).headers.get('content-type')
); |
[Equivalent to above.] |
{
const url = 'https://example.com/';
const response =
await fetch(url, { method: 'HEAD' });
const contentType =
response.headers.get('content-type');
console.log(contentType);
} |
'https://pk.example/berlin-calling'
|> await fetch(#, { mode: 'cors' })
|> do {
if (#.headers.get('content-type')
&& #.headers.get('content-type')
.toLowerCase()
.indexOf('application/json') >= 0
)
return #.json();
else
throw new TypeError();
}
|> await #.json()
|> processJSON; This example uses |
fetch('https://pk.example/berlin-calling',
{ mode: 'cors' }
).then(response => {
if (response.headers.get('content-type')
&& response.headers.get('content-type')
.toLowerCase()
.indexOf('application/json') >= 0
)
return response.json();
else
throw new TypeError();
}).then(processJSON); |
As the single most-used JavaScript library in the world, jQuery has provided an alternative human-ergonomic API to the DOM since 2006. jQuery is under the stewardship of the JS Foundation, a member organization of TC39 through which jQuery’s developers are represented in TC39. jQuery’s API requires complex data processing that becomes more readable with smart pipelines.
With smart pipelines | Status quo |
---|---|
return data
|> buildFragment([#], context, scripts)
|> #.childNodes
|> jQuery.merge([], #); The path that a reader’s eyes must trace while reading this pipeline moves
straight down, with some movement toward the right then back: from |
parsed = buildFragment(
[ data ], context, scripts
);
return jQuery.merge(
[], parsed.childNodes
); From jquery/src/core/parseHTML.js. In this code, the eyes first must look
for |
(key |> toType) === 'object'; key |> toType |> # === 'object';
|
toType(key) === 'object'; |
context = context
|> # instanceof jQuery
? #[0] : #; |
context =
context instanceof jQuery
? context[0] : context; |
context
|> # && #.nodeType
? #.ownerDocument || #
: document
|> jQuery.parseHTML(match[1], #, true)
|> jQuery.merge; |
jQuery.merge(
this, jQuery.parseHTML(
match[1],
context && context.nodeType
? context.ownerDocument
|| context
: document,
true
)
); From jquery/src/core/init.js. |
match
|> context[#]
|> (this[match] |> isFunction)
? this[match](#);
: this.attr(match, #); Note how, in this version, the parallelism between the two clauses is very
clear: they both share the form |
if (isFunction(this[match])) {
this[match](context[match]);
} else
this.attr(match, context[match]);
} From jquery/src/core/init.js. Here, the parallelism between the clauses
is somewhat less clear: the common expression |
elem = match[2]
|> document.getElementById; |
elem = document.getElementById(match[2]); From jquery/src/core/init.js. |
// Handle HTML strings
if (…)
…
// Handle $(expr, $(...))
else if (!# || #.jquery)
return context
|> # || root
|> #.find(selector);
// Handle $(expr, context)
else
return context
|> this.constructor
|> #.find(selector); The parallelism between the final two clauses becomes clearer here too.
They both are of the form |
// Handle HTML strings
if (…)
…
// Handle $(expr, $(...))
else if (!context || context.jquery)
return (context || root).find(selector);
// Handle $(expr, context)
else
return this.constructor(context)
.find(selector); From jquery/src/core/init.js. The parallelism is much less clear here. |
Underscore.js is another utility library very widely used since 2009, providing numerous functions that manipulate arrays, objects, and other functions. It too has a codebase that transforms values through many expressions – a codebase whose readability would therefore benefit from smart pipelines.
With smart pipelines | Status quo |
---|---|
function (obj, pred, context) {
return obj
|> isArrayLike
|> # ? _.findIndex : _.findKey
|> #(obj, pred, context)
|> (# !== void 0 && # !== -1)
? obj[#] : undefined;
} |
function (obj, pred, context) {
var key;
if (isArrayLike(obj)) {
key = _.findIndex(obj, pred, context);
} else {
key = _.findKey(obj, pred, context);
}
if (key !== void 0 && key !== -1)
return obj[key];
} |
function (obj, pred, context) {
return pred
|> cb
|> _.negate
|> _.filter(obj, #, context);
} |
function (obj, pred, context) {
return _.filter(obj,
_.negate(cb(pred)),
context
);
} |
function (
srcFn, boundFn, ctxt, callingCtxt, args
) {
if (!(callingCtxt instanceof boundFn))
return srcFn.apply(ctxt, args);
var self = srcFn
|> #.prototype |> baseCreate;
return self
|> srcFn.apply(#, args)
|> _.isObject(#) ? # : self;
} |
function (
srcFn, boundFn,
ctxt, callingCtxt, args
) {
if (!(callingCtxt instanceof boundFn))
return srcFn.apply(ctxt, args);
var self = baseCreate(srcFn.prototype);
var result = srcFn.apply(self, args);
if (_.isObject(result)) return result;
return self;
} |
function (obj) {
return obj
|> # == null
? 0
: #|> isArrayLike
? #|> #.length
: #|> _.keys |> #.length;
};
} Smart pipelines make parallelism between all three clauses becomes clearer: This particular example becomes even clearer when paired with Additional Feature BP. |
function (obj) {
if (obj == null) return 0;
return isArrayLike(obj)
? obj.length
: _.keys(obj).length;
} |
Lodash is a fork of Underscore.js that remains under rapid active development. Along with Underscore.js’ other utility functions, Lodash provides many other high-order functions that attempt to make functional programming more ergonomic. Like jQuery, Lodash is under the stewardship of the JS Foundation, a member organization of TC39, through which Lodash’s developers also have TC39 representation. And like jQuery and Underscore.js, Lodash’s API involves complex data processing that becomes more readable with smart pipelines.
With smart pipelines | Status quo |
---|---|
function hashGet (key) {
return this.__data__
|> nativeCreate
? (#[key] === HASH_UNDEFINED
? undefined : #)
: hashOwnProperty.call(#, key)
? #[key]
: undefined;
} |
function hashGet (key) {
var data = this.__data__;
if (nativeCreate) {
var result = data[key];
return result === HASH_UNDEFINED
? undefined : result;
}
return hasOwnProperty.call(data, key)
? data[key] : undefined;
} |
function listCacheHas (key) {
return this.__data__
|> assocIndexOf(#, key)
|> # > -1;
} |
function listCacheHas (key) {
return assocIndexOf(this.__data__, key)
> -1;
} |
function mapCacheDelete (key) {
const result = key
|> getMapData(this, #)
|> #['delete']
|> #(key);
this.size -= result ? 1 : 0;
return result;
} |
function mapCacheDelete (key) {
var result =
getMapData(this, key)['delete'](key);
this.size -= result ? 1 : 0;
return result;
} |
function castPath (value, object) {
return value |>
#|> isArray
? #
: (#|> isKey(#, object))
? [#]
: #|> toString |> stringToPath;
} |
function castPath (value, object) {
if (isArray(value)) {
return value;
}
return isKey(value, object)
? [value]
: stringToPath(toString(value));
} |