Risky question to be opinionated. I'm working on a project with Ramda.js. And I see many ifElse calls throughout the code.
const getEvent = R.ifElse(
fireable,
R.always(sendAnalyticsEvent),
R.always(R.always(undefined))
);
Is this really worth the effort to wrap logic in a functional conditional like this? Is there a benefit?
If ultimately Ramda just abstracts a ternary operation and we return undefined on false match.
Ramdas ifElse
var ifElse = _curry3(function ifElse(condition, onTrue, onFalse) {
return curryN(Math.max(condition.length, onTrue.length, onFalse.length),
function _ifElse() {
return condition.apply(this, arguments) ? onTrue.apply(this, arguments) : onFalse.apply(this, arguments);
}
);
});
export default ifElse;
This seems like an anitpattern in the FP world, always returning undefined or in some cases null
R.ifElse(hasUrl, promptToShare, R.always(null))
Regardless of the questionable return of undefined, wouldn't using ternary operators be more idiomatic to a javascript community?
hasUrl(urlObject) ? promptToShare() : null
This seems more succinct and legible to me, I want to refactor. But this could be due to my naivety of the FP world.
Several points (disclaimer: I'm a Ramda author):
Far too often, you're right. Point-free code is overused when new users pick up FP in Javascript. I generally suggest that it's only useful when it improves readability.
An ifElse invocation is not equivalent to a Javascript conditional expression (ternary.) It can be equivalent to a lambda function that returns the value of a ternary, however. That is, the example would be more like (urlObject) => hasUrl(urlObject) ? promptToShare(urlObject) : null. At this point, the ifElse is at least possibly more readable. This brings us to the points from the comments about partial application/currying
There is little reason I can see to use ifElse with functions that have different signatures. That is, if promptToShare takes no arguments, then it probably doesn't belong in a call to ifElse.
That getEvent function looks quite bizarre. Given that it uses a name like sendAnalyticsEvent, I'm guessing that it creates some side-effect. And the other branch is a no-op. While the Ramda team doesn't really care how you use the library, this is not the sort of function we envision users creating with it.
I've seen other odd calls to ifElse passing identity for one of the branch functions. Those should presumably be replaced with when or unless, which would certainly be more semantic.
So I agree that your example does not need ifElse at all. But ifElse and its peers when and unless do have their places.
Related
This article explains why I have a warning if I use a code like this:
var htmlCollection = document.getElementsByClassName("class-name"),
i = htmlCollection.length,
htmlElement;
// Because htmlCollection is Live, we use a reverse iteration.
while (htmlElement = htmlCollection[--i]) { // **Warning?! Why?!**
htmlElement.classList.remove("class-name");
}
But this no explaination about « why is a bad practice to assignment expression in a while condition? ».
I also read this stackoverflow answers that point this practice as good. So...
There is a performance problem with while (element = element.parentNode) syntax-like or is just a style-code recommandation?
By the way, seems the « --i » operator is also a bad practice. I read in this article :
The ++ (increment) and -- (decrement) operators have been known to contribute to bad code by encouraging excessive trickiness.
It's some sort of joke?
There should be no performance problems with it (arguably, indexing with prefix increment can be slightly slower than postfix increment, due to issues with CPU pipelines; this is a microoptimization so ridiculously micro that it almost certainly means nothing in the context of JS engine overhead, even in C the compiler is likely to reorder expressions if it can to ensure it's not stalled waiting on the increment).
Either way, the main argument against assignment in a conditional is basically that most of the time when you do it, it's a mistake (you meant == or in JS, ===). Some code checkers (and C# requires this as a language feature to avoid accidents) are satisfied if you wrap the assignment in an additional layer of parens, to say, "Yup, I really meant to assign" (which is also necessary when you're comparing the result of the assignment to some other value; omitting the parens would instead compare, then assign a boolean, which even more likely to be wrong).
Some people have a hate on for increment/decrement operators used as part of larger expressions, because remembering order of operations is hard I guess, and because C programmers have been known to write horrible things like ++*++var and the like. I ignore these people; just don't use it for excessively tricky things.
As an orthogonal approach, and possible 'cleaner/clearer' there is:
// var htmlCollection = document.getElementsByClassName("class-name");
var htmlCollection = document.querySelectorAll('.class-name');
for(let htmlElement of htmlCollection) {
htmlElement.classList.remove("class-name");
}
as a method of iterating over DOM elements.
UPDATED to include suggestion from ShadowRanger below.
Many languages have an operator that allows you to pipe the results of one operation into a call to another (e.g. the | operator in bash, the |> operator in F#).
One of the great advantages to my mind of the common idiom of method chaining in javascript is that it reads top-to-bottom, left-to-right:
var fooOddSquares = [1, 2, 3, 4, 5]
.filter(x => x % 2)
.map(x => "foo" + x * x)
.reduce(((acc, str, i) => acc[i + 1] = str; return acc), {});
// => {1: "foo1", 2: "foo9", 3: "foo25"}
compared to compositional code:
var something = func5(
func4(
func3(
func2(
func1(
somedata
)
)
)
)
);
which reads right-to-left, bottom-to-top. I realize this could be cleaned up via function composition, but that's not necessarily the point. Just to be absolutely clear with what I'm looking for:
var something = func1(somedata)
|> func2
|> func3
|> func4
//etc...
Doing a google search on the pipe operator in javascript mostly turns up info about the bitwise OR operation. With some digging however I was able to dig up this article describing a dirty-hacked version of operator overloading that could implement something of what I'm talking about. I also unearthed this gist that describes said operator and says that "it has been proposed for javascript".
Looking at ES 2016, I see proposals for an exponentiation operator and the bind operator. Both are useful, but not what I want. So per the headline in the gist, has anyone actually proposed this for javascript?
A pipeline operator has been proposed for ES7 (ES2016) in December 2015.
https://github.com/mindeavor/es-pipeline-operator
As mentioned already a pipeline operator has been proposed for ES7 (2016), however that doesn't help much if you want to use something like this right now with babel which exactly how I came across this question 9 months later.
The biggest hitch in supporting the es-pipeline-operator proposal through babel as far as I know is the current inability to use |> or <| as operators which create syntax errors and cannot be fixed without changes to babel and unfortunately does not look like the problem will be resolved any time soon.
I would personally like to see the pipe backward operator added to the proposal as well since both forward and backward are useful in different situations.
For example, I use the pipe backward operator when modifying functions or anywhere I would typically use "compose" over "pipe" which I prefer in certain situations for readability.
const something = curry <| function(state, pattern) {
// something
}
const something = function(state, pattern) {
// something
} |> curry
Because these pipe operators are incredibly useful in functional style javascript programming and for the benefit of anyone who came looking for a solution to use this right now like me, I have made a babel plugin that uses the bitwise operators << and >> which I rarely use to achieve both forward and backward piping for the time being and in the rare cases where the bitwise operators are required the use of the "no pipe"; directive will disable the plugin for a certain scope.
https://github.com/michaelmitchell/babel-plugin-pipe-composition
This github repo and my favorite issue within it ;) discuss just that.
The proposal has been moving around in a small neighborhood of ideas for some months, but centers around using -> and :: as sugar around Function.prototype.apply and Function.prototype.bind, very roughly.
The current draft is for :: to sit between a scope and function (instance::method) and act much like instance.method.bind(instance), locking the this scope of the function for any calls. Alongside that, -> may be defined to pass a scope and array of arguments (as apply), so instance->method(foo, bar) would desugar to instance.method.apply(instance, [foo, bar]). At least, that's one of the directions that is being discussed (disclosure: that's my take on it).
I usually just go like this:
var something = [somedata].map(func1).map(func2).map(func3).map(func4)[0];
It solves one of the main points in the question
One of the great advantages to my mind of the common idiom of method chaining in javascript is that it reads top-to-bottom, left-to-right:` by taking advantage of how this already exists for arrays.
because this feature already exists for arrays, and converting something into an array, and back again is easy enough.
the above uses the names in the question, but here is a concrete example:
console.log([9.3].map(Math.floor).map(Math.sqrt)[0]);
// or even this:
[9.3].map(Math.floor).map(Math.sqrt).forEach(x => console.log(x));
I learned today that forEach() returns undefined. What a waste!
If it returned the original array, it would be far more flexible without breaking any existing code. Is there any reason forEach returns undefined.
Is there anyway to chain forEach with other methods like map & filter?
For example:
var obj = someThing.keys()
.filter(someFilter)
.forEach(passToAnotherObject)
.map(transformKeys)
.reduce(reduction)
Wouldn't work because the forEach doesn't want to play nice, requiring you to run all the methods before the forEach again to get the object in the state needed for the forEach.
What you want is known as method cascading via method chaining. Describing them in brief:
Method chaining is when a method returns an object that has another method that you immediately invoke. For example, using jQuery:
$("#person")
.slideDown("slow")
.addClass("grouped")
.css("margin-left", "11px");
Method cascading is when multiple methods are called on the same object. For example, in some languages you can do:
foo
..bar()
..baz();
Which is equivalent to the following in JavaScript:
foo.bar();
foo.baz();
JavaScript doesn't have any special syntax for method cascading. However, you can simulate method cascading using method chaining if the first method call returns this. For example, in the following code if bar returns this (i.e. foo) then chaining is equivalent to cascading:
foo
.bar()
.baz();
Some methods like filter and map are chainable but not cascadable because they return a new array, but not the original array.
On the other hand the forEach function is not chainable because it doesn't return a new object. Now, the question arises whether forEach should be cascadable or not.
Currently, forEach is not cascadable. However, that's not really a problem as you can simply save the result of the intermediate array in a variable and use that later:
var arr = someThing.keys()
.filter(someFilter);
arr.forEach(passToAnotherObject);
var obj = arr
.map(transformKeys)
.reduce(reduction);
Yes, this solution looks uglier than the your desired solution. However, I like it more than your code for several reasons:
It is consistent because chainable methods are not mixed with cascadable methods. Hence, it promotes a functional style of programming (i.e. programming with no side effects).
Cascading is inherently an effectful operation because you are calling a method and ignoring the result. Hence, you're calling the operation for its side effects and not for its result.
On the other hand, chainable functions like map and filter don't have any side effects (if their input function doesn't have any side effects). They are used solely for their results.
In my humble opinion, mixing chainable methods like map and filter with cascadable functions like forEach (if it was cascadable) is sacrilege because it would introduce side effects in an otherwise pure transformation.
It is explicit. As The Zen of Python teaches us, “Explicit is better than implicit.” Method cascading is just syntactic sugar. It is implicit and it comes at a cost. The cost is complexity.
Now, you might argue that my code looks more complex than yours. If so, you would be judging the book by its cover. In their famous paper Out of the Tar Pit, the authors Ben Moseley and Peter Marks describe different types of software complexities.
The second biggest software complexity on their list is complexity caused by explicit concern with control flow. For example:
var obj = someThing.keys()
.filter(someFilter)
.forEach(passToAnotherObject)
.map(transformKeys)
.reduce(reduction);
The above program is explicitly concerned with control flow because you are explicit stating that .forEach(passToAnotherObject) should happen before .map(transformKeys) even though it shouldn't have any effect on the overall transformation.
In fact, you can remove it from the equation altogether and it wouldn't make any difference:
var obj = someThing.keys()
.filter(someFilter)
.map(transformKeys)
.reduce(reduction);
This suggests that the .forEach(passToAnotherObject) didn't have any business being in the equation in the first place. Since it's a side effectful operation, it should be kept separate from pure code.
When you write it explicitly as I did above, not only are you separating pure code from side effectful code but also you can choose when to evaluate each computation. For example:
var arr = someThing.keys()
.filter(someFilter);
var obj = arr
.map(transformKeys)
.reduce(reduction);
arr.forEach(passToAnotherObject); // evaluate after pure computation
Yes, you are still explicitly concerned with control flow. However, at least now you know that .forEach(passToAnotherObject) has nothing to do with the other transformations.
Thus, you have eliminated some (but not all) of the complexity caused by explicit concern with control flow.
For these reasons, I believe that the current implementation of forEach is actually beneficial because it prevents you from writing code that introduces complexity due to explicit concern with control flow.
I know from personal experience from when I used to work at BrowserStack that explicit concern with control flow is a big problem in large-scale software applications. It is indeed a real world problem.
It's easy to write complex code because complex code is usually shorter (implicit) code. So it's always tempting to drop in a side effectful function like forEach in the middle of a pure computation because it requires less code refactoring.
However, in the long run it makes your program more complex. Think of what would happen a few years down the line when you quit the company that you work for and somebody else has to maintain your code. Your code now looks like:
var obj = someThing.keys()
.filter(someFilter)
.forEach(passToAnotherObject)
.forEach(doSomething)
.map(transformKeys)
.forEach(doSomethingElse)
.reduce(reduction);
The person reading your code now has to assume that all the additional forEach methods in your chain are essential, put in extra work to understand what each function does, figure out by herself that these extra forEach methods are not essential to compute obj, eliminate them from her mental model of your code and only concentrate on the essential parts.
That's a lot of unnecessary complexity added to your program, and you thought that it was making your program more simple.
It's easy to implement a chainable forEach function:
Array.prototype.forEachChain = function () {
this.forEach(...arguments);
return this;
};
const arr = [1,2,3,4];
const dbl = (v, i, a) => {
a[i] = 2 * v;
};
arr.forEachChain(dbl).forEachChain(dbl);
console.log(arr); // [4,8,12,16]
Suppose we have these two async functions:
function myfu(f) {
setTimeout(function(){ f(true) }, 200)
}
function myfu2(x, f) {
setTimeout(function(){ f(500 + x) }, 200)
}
I was thinking about this syntax:
if (myfu.()) {
var d = myfu2.(55)
console.log(d) // outputs 555
}
which is amazingly simpler to type and read that this:
myfu(function (DOTCALL1) {
if (DOTCALL1) {
myfu2(55, function (DOTCALL2) {
d = DOTCALL2
console.log(d) // outputs 555
})
}
})
The conversion from .( syntax could be easily implemented by means of search/replace/regex.
I noticed that most of the time the result of my asynchronous functions is not used, and after it was called, the upper functions also exits immediately.
Although sometimes the upper function can do something after it called the first async(callback), usually it does nothing. Sometimes the asynchronous function could return something useful, but most of the time it just returns undefined and real stuff is returned with callback(result).
So I thought that maybe I could modify the code some how to make it easier to type and read. Which is illustrated by the example above.
My question is sipmle: is there already similar solution? I do not want to reinvent the wheel (if it is already invented). Or, if there is no such project, why not, why this approach, which seems so easy to implement and supposed to simplify development significantly is not used?
Please do not respond with the suggestion to "take a look at async.js". I am not asking about asynchronous programming patterns, asynchronous programming in general, not about arrow function notation =>, not about coffeScript. I am asking about a code conversion on the text level from proposed original syntax form to well known async ladder, aka "callback hell" notation.
What you are describing is very similar to streamline.js: https://github.com/Sage/streamlinejs.
I wrote it but I got the idea from narrative.js: See http://www.neilmix.com/narrativejs/doc/
So your intuition is right. This can be done. It is nevertheless a little more complicated than a few search/replace/regex operations, at least if you want to go beyond the obvious and support async calls in all JavaScript constructs. For example, try to rewrite:
while (asyncF1.() && asyncF2.()) asyncF3.();
I described the streamline transformation algorithm in a blog post: https://bjouhier.wordpress.com/2011/05/24/yield-resume-vs-asynchronous-callbacks/
I have this site bookmarked since last year where the author blogs about EcmaScript6 which allegedly allows you to obtain something similar to .NET's async / await pattern (so allowing code to invoke asynchronous methods like a "normal" one). Here is the link
ES6 introduces two small extensions to the language syntax:
function*: the functions that you declare with a little twinkling star are generator functions. They execute in an unusual way and return generators.
yield: this keyword lets you transfer control from a generator to the function that controls it.
And, even though these two language constructs were not orginally designed to have the async/await semantics found in other languages, it is possible to give them these semantics:
The * in function* is your async keyword.
yield is your await keyword.
Knowing this, you can write asynchronous code as if JavaScript had async/await keywords.
I never had the chance to try it out though, so YMMV. It sounds useful.
I'm curious how my game would look like in functional style instead of OOP.
Here are core lines of code in Node.js:
if (!player.owns(flag) && player.near(flag) && flag.isUnlocked()) {
player.capture(flag);
}
My guess was, it could look like this:
var canCapture = [not(owns), isNear, canUnlock].every(function(cond) {
return cond(playerData, flagData);
});
if(canCapture) {
// how to capture?
}
But not sure, as not experienced functional coder. I'm interested in every answer close to the subject (it can be even in other programming style).
It could look somewhat like this:
if (!player.owns(flag) && player.near(flag) && flag.isUnlocked()) {
capturingPlayer = player.capture(flag);
}
where capturingPlayer is a new object, whose difference to player is that is has captured a flag. player is unmodified by the call to capture.
If you prefer a "non-OO" syntax (whatever that could mean)
if (!owns(player, flag) && near(player, flag) && isUnlocked(flag)) {
capturingPlayer = capture(player, flag);
}
To expand and hopefully clarify a bit:
Functional programming, in the sense employed by the functional programming community, does not just mean "functions/procedures are first-class objects".
What it does mean is that functions are functions in the mathematical sense, i.e.
All functions return a value.
Every function returns the same value every time it's passed the same arguments.
A function has no side-effects whatsoever - there are no mutable objects or assignment.
So, as long as none of your object's methods mutate the object, you don't really need to change much to program in a "functional style".
Edit:
Unfortunately both "functional" and "object-oriented" (in particular) are pretty ill-defined concepts.
Try and find a definition of "object-oriented" - there are at least as many definitions as there are people attempting to define it.
To get an understanding of functional programming, read Why functional programming matters by John Hughes, at least twice.