I want to create a javascript pipeline like powershell, bash (|) or f# (|>). Ie. something equivalent to
getstuff() | sort() | grep("foo") | take(5)
I saw a discussion about this in coffeescript forum but in the end they shelved it because everybody said that you could do the same thing with function chaining. But as far as I can see that requires getstuff returns something that has a sort method on it; the sort method must return something that has grep method on it etc. This is pretty restrictive as it requires all potential pipeline members to know about each other in advance. I know JavaScript has some pretty clever tricks in it and I am still at the 101 level - so is this doable
getstuff().sort.().grep().take()
without that constraint
is this doable
getstuff().sort.().grep().take()
without that constraint
No.
I like short answers! Can you suggest any way that something like it could be done
At a high level, you could do something similar to what jQuery does under the hood to allow chaining. Create an array-like wrapper object type which has all of the functions you want to be able to call; each successive chained call can operate on an internal stack in addition to the explicitly-passed arguments.
Not to keep beating the dead jQuery horse, but one of the best ways to understand what I'm talking about is to just start digging through the jQuery core source code and figure out how the chaining works.
Defining an object to support the kind of function chaining you want is actually quite easy:
getStuff = ->
sort: ->
# set #stuff...
this
grep: (str) ->
# modify #stuff...
this
take: (num) ->
#stuff[num]
That's all you need to make getstuff().sort.().grep('foo').take(5) work.
You can make those calls without worrying about the return values having the appropriate methods like so:
take(5, grep("foo", sort(getstuff())));
But, that doesn't get through the problem of each function needing to be passed data that is meaningful to it. Even JavaScript isn't that slippery. You can call sort() on an image (for example,) but there's no meaningful way to generate results.
You could do something similar by returning a special object that has all required methods on it, but can be used instead of the final value. For example, you could return an Array instance that has all these methods on it.
var getstuff = function () {
obj = Array.apply(this, arguments);
obj.take = function (n) {
return this[n];
};
obj.grep = function (regexp) {
return getstuff.apply(this, Array.prototype.filter.apply(this, [function (item) {
return item.toString().search(regexp) !== -1;
}]));
};
obj.splice = function () {
return getstuff.apply(this, Array.prototype.splice.apply(this, arguments));
}
return obj;
}
// shows [-8, 1]
console.log(getstuff(3, 1, 2, 'b', -8).sort().grep(/\d+/).splice(0, 2));
// shows 3
var stuff = getstuff(3, 1, 2, 'b', -8).grep(/\d+/);
console.log(stuff.sort()[stuff.length]);
Note that the above is not a particularly fast implementation, but it returns arrays with special methods by still keeping the global Allay's prototype clean, so it won't interfere with other code.
You could make it faster by defining these special methods on the Array.prototype, but you should be careful with that...
Or, if your browser supports subclassing Array, then all you need is a supclass and a handy constructor, getstuff().
Related
I have created a node module with a couple of custom methods for arrays and strings.
First I just used it like a regular module and got the functions from a require like this:
Alt 1.
const invSlice = require('inverted-slice');
let arr1 = [1,2,3,4];
invSlice.iSlice(arr, start, stop);
This works but it would be nicer to call iSlice as a method on the Array object. I solved this by adding the following code in my library:
Array.prototype.iSlice = iSliceBuiltin; // iSliceBuiltin is my function
And the method can now be used like:
Alt 2.
require('inverted-slice');
let arr1 = [1,2,3,4];
arr1.iSlice(start, stop);
Which I think is nicer then Alt 1.
Question
My question is if there is any best practice or guidelines to follow when adding custom methods like in Alt 2 to built-in Objects like Array or String ?
Extending built-in prototypes has always triggered debates, and I think we can conclude it is not considered best practice.
On the other hand it is indeed nice if you can call these custom methods as object methods instead of plain functions.
You might consider a wrapper function that will return an Array instance that has the extra methods defined for it: i.e., not on the prototype, but on the Array instance itself.
Your module could look like this:
function iArray(arr) {
return Object.assign([], arr || [], {
iSlice: iSliceBuiltin,
iSplice: iSpliceBuiltin
});
}
// ... your module functions come here, but excluding the changes to the Array prototype
module.exports = {
iArray
}
Then you would use it like this:
const iArray = require('inverted-slice');
let arr1 = iArray([1,2,3,4]); // enrich array with extra methods
let result = arr1.iSlice(0, 1);
To allow chaining, you could change the return statement in iSliceSpliceHelper to:
return iArray(newArr);
So, now you can write:
let arr1 = iArray([1,2,3,4]); // enrich array with extra methods
let result = arr1.iSlice(0, 1).iSlice(1, 2);
Existing libraries might implement your alternative 1 (e.g. underscore), but many also go for something like I propose here. See for instance Sugar (new Sugar.Array([1,2,3])), or Lazy (Lazy([1,2,3])).
In small doses I think it's not that big of a deal to use Alt 2, but I believe that over usage can create problems. If I remember correctly, they had to completely redo Cut The Rope due to performance problems that I believe stemmed largely in part from prototype extensions. You may also want to consider posting this on https://codereview.stackexchange.com/
A couple references:
http://perfectionkills.com/whats-wrong-with-extending-the-dom/
https://softwareengineering.stackexchange.com/questions/104320/why-is-extending-the-dom-built-in-object-prototypes-a-bad-idea
Please forgive me, I'll do my best to describe what I am looking for. I don't have a name for it, so it makes it that much more difficult.
Looking at libraries such as Folktale and monet.js, I like being able to chain operations with map without null-checking or if statements, using e.g. Maybe, Either, Task.
I am wondering whether a similar construct exists to solve the following problem:
I have an initial input.
I want to construct a chain of functions that operate on the input.
Each function may or may not return a result.
If a function does not return a result, call the next function in the chain.
As soon as a function returns a result, ignore the rest of the functions in the chain (similarly to mapping a function on a Maybe.Nothing)
Return the result.
In other words, I'm looking for something similar to Maybe which holds a value and maps a function on Just but ignores a function mapped on Nothing, and you can extract the value. I'm looking for something that holds an input and an initial null result. When you map a function, it runs the function on the input only if result is null. If the function returns a value, that becomes the result and any other functions that are mapped are ignored. Finally, you can extract the result.
In imperative programming, this might look like:
var result1 = function1(input);
if (result1) {
return result1;
}
var result2 = function2(input);
if (result2) {
return result2;
}
// and so on.
Instead, I'd like to construct something along the lines of
Something(input).map(function1).map(function2).result()
or
compose(result, map(compose(function2, function1))(Something(input))
Does such a construct exist? Does it have a name? Even if this doesn't exist and I could write it myself, I'm at a loss as to what to call it. Suggestions welcome.
Thanks for your help!
UPDATE
Following the solution by #Bergi I used Maybe.orElse. I wrote a little helper called ShortCircuit, which I am posting below in case anyone finds this useful.
import Maybe from "data.maybe";
const ShortCircuit = function(input, result) {
this.input = input;
this.result = result;
};
ShortCircuit.of = function(input, f) {
return new ShortCircuit(input, Maybe.fromNullable(f(input)));
};
ShortCircuit.prototype.orElseMaybe = function(f) {
return new ShortCircuit(this.input, this.result.orElse(() => Maybe.fromNullable(f(this.input))));
};
ShortCircuit.prototype.get = function() {
return this.result.get();
};
export default ShortCircuit;
And here is how you use it:
const result = ShortCircuit.of(input, f1).orElseMaybe(f2).orElseMaybe(f3).get();
Suggestions for improvement are welcome.
What you are looking for is known as the Alternative type class in Haskell. I have not yet found1 a JS library implementing it, though there is support for it in PureScript and a discussion about specifying it in FantasyLand.
Regardless, I've found a function in Folktale that does exactly what you want: Maybe's orElse. You'd use it like this:
Something(input).orElse(function1).orElse(function2).get() // `get` throws when all failed
Even better, orElse is available not only for Maybe but also on Either, Validation and Task!
The getOrElse method, known as orElse in Monet.js, is not really helpful as it wouldn't execute your "handlers" only in the case of a fail. You can implement your own Alternative though using the cata method.
1: Googling "JS alternative" is not really helpful either, even when you know the name :-)
Oh, and just in case you are using promises, there's always the .catch(…) method which has broad (ES6-backed) support.
Say I have a step in a procedure that requires the retrieval of two objects. I would use join() to coordinate the retrievals:
return promise.join(retrieveA(), retrieveB())
.spread(function(A, B) {
// create something out of A and B
});
The documentation shows that you can also pass the handler as the last parameter:
return promise.join(retrieveA(), retrieveB(), function(A, B) {
// create something out of A and B
});
I'm curious as to what the rationale behind the existence of this option.
Fact time: The reason .join was added was to make #spion happy. Not without reason though, using .join means you have a static and known number of promise which makes using it with TypeScript a lot easier. Petka (Esailija) liked the idea and also the fact it can be optimised further because it doesn't have to abide to weird guarantees the other form does have to abide to.
Over time, people started (at least me) using it for other use cases - namely using promises as proxies.
So, let's talk about what it does better:
Static Analysis
It's hard to statically analyse Promise.all since it works on an array with an unknown types of promises of potentially different types. Promise.join can be typed since it can be seen as taking a tuple - so for example for the 3 promises case you can give it a type signature of (Promise<S>, Promise<U>, Promise<T>, ((S,U,T) -> Promise<K> | K)) -> Promise<K> which simply can't be done in a type safe way for Promise.all.
Proxying
It's very clean to use when writing promise code in the proxying style:
var user = getUser();
var comments = user.then(getComments);
var related = Promise.join(user, comments, getRelated);
Promise.join(user, comments, related, (user, comments, related) => {
// use all 3 here
});
It's faster
Since it doesn't need to produce the value of the given promises cached and to keep all the checks .all(...).spread(...) does - it'll perform slightly faster.
But... you really usually shouldn't care.
you can also pass the handler as the last parameter. I'm curious as to what the rationale behind the existence of this option.
It is not an "option". It's the sole purpose of the join function.
Promise.join(promiseA, promiseB, …, function(a, b, …) { … })
is exactly equivalent to
Promise.all([promiseA, promiseB, …]).spread(function(a, b, …) { … })
But, as mentioned in the documentation, it
is much easier (and more performant) to use when you have a fixed amount of discrete promises
It relieves you of needing to use that array literal, and it doesn't need to create that intermediate promise object for the array result.
I was reading the source code for pallet.js and came across this.
var ret = (function(proto) {
return {
slice: function(arr, opt_begin, opt_end) {
return proto.slice.apply(arr, proto.slice.call(arguments, 1));
},
extend: function(arr, arr2) {
proto.push.apply(arr, arr2);
}
};
})(Array.prototype);
var slice = ret.slice;
var extend = ret.extend;
Why is this necessary? Why could they not simply write this:
var slice = function(arr,opt_begin,opt_end) {
return Array.prototype.slice.apply(arr,[opt_begin,opt_end]));
}
var extend = function(arr,arr2) {
return Array.prototype.push.apply(arr,arr2);
}
EDIT 1:
In response to the duplicate question. I don't think it is a duplicate, but that question definitely does address my question. So it is an optimization. But won't each one only be evaluated once? So is there really a significant improvement here for two function calls?
Also if we are worried about performance why are we calling proto.slice.call(arguments,1) instead of constructing the array of two elements by hand [opt_begin,opt_end], is slice faster?
Because the syntax is just so much cooler. Plus you can rationalize it's use by telling yourself that it's more DRY. You didn't have to type Array.prototype twice.
I can't be sure what was the original rationale behind that code (only the author knows) but I can see a few differences:
proto is a closed-over local variable, while instead Array is a global. It's possible for a smart enough Javascript engine to optimize access because proto is never changed and thus it could even be captured by value, not reference. proto.slice can be faster than Array.prototype.slice because one lookup less is needed.
passing opt_begin and opt_end as undefined is not the same as not passing them in general. The called function can know if a parameter was passed and happens to be undefined or if instead it wasn't passed. Using proto.slice.call(arguments, 1) ensures that the parameters are passed to slice only if they were actually passed to the closure.
I saw these 2 basic ways of namespacing in JavaScript.
Using object:
var Namespace = { };
Namespace.Class1 = function() { ... };
Using function:
function Namespace() { };
Namespace.Class1 = function() { ... };
How do they differ? Thank you.
As others have pointed out, a function is an object so the two forms can be interchangeable. As a side note, jQuery utilizes the function-as-namespace approach in order to support invocation and namespacing (in case you're wondering who else does that sort of thing or why).
However with the function-as-namespace approach, there are reserved properties that should not be touched or are otherwise immutable:
function Namespace(){}
Namespace.name = "foo"; // does nothing, "name" is immutable
Namespace.length = 3; // does nothing, "length" is immutable
Namespace.caller = "me"; // does nothing, "caller" is immutable
Namespace.call = "1-800-555-5555" // prob not a good idea, because...
// some user of your library tries to invoke the
// standard "call()" method available on functions...
Namespace.call(this, arg); // Boom, TypeError
These properties do not intersect with Object so the object-as-namespace approach will not have these behaviours.
The first one declares a simple object while the second one declares a function. In JavaScript, functions are also objects, so there is almost no difference between the two except that in the second example you can call Namespace() as a function.
Well, if all you're doing us using that "Namespace" thing as a way to "contain" other names, then those two approaches are pretty much exactly the same. A function instance is just an object, after all.
Now, generally one would use a function like that if the function itself were to be used as a constructor, or as a "focal point" for a library (as is the case with jQuery).
They don't. Functions are "first class objects". All this means is that conceptually and internally they are stored and used in the same ways. Casablanca's point of one difference you can call it as a function is a good one though. You can also test for whether or not the class was defined through a function with the typeof operator.
typeof {}
returns "object"
typeof (function())
returns "function"