My goal here is not to convince you. If you disagree, you should
probably keep disagreeing. However, my goal is to show that it is
possible for someone to find try/catch/throw distasteful, and not for
lack of understanding or some mental deficiency. (Unless you think
that I don't understand JavaScript, or am mentally deficient, in which
case you shouldn't take anything I say seriously anyway. I realize
I'm asking for a little bit of credit.)
In my experience, after using it in a few different forms in different
languages, and occasionally with some pleasure and happiness, I have
settled in the last few years on the sentiment that try/catch is a
mistake, and an anti-pattern. Quite a while before coming to Node, I
was already pretty dead-set against the overuse of try/catch/throw in
JavaScript, just dealing with client-side code. I think there even
may be a rant from ca 2008 or so in Yahoo's devel-frontend archives on
the topic.
Throw is great. I love throw. Throw is how you crash a program. It
should be like assert(0) in C. If it's not worth crashing your
program, on par with a SyntaxError or something else that is a clear
indication of a programmer mistake (and not, say, invalid data
encountered at run time), it should not throw. It's great to litter
code with asserts, even if it seems needless at the time, because it
saves you so much debugging time later down the road.
The fact that JSON.parse throws is a bit hideous. There is no
JSON.validate to check for syntax errors, so having JSON.parse throw
means that you *can't* interpret user-supplied JSON without a
try/catch. I much prefer php's json_decode function, since it just
returns `null` on invalid input. You can't reasonably argue that
this:
try {
foo = JSON.parse(input)
} catch (er) {
return "invalid data"
}
is more lightweight than:
foo = JSON.parse(input)
if (!foo) return "invalid data"
Try/catch is goto wrapped in pretty braces. There's no way to
continue where you left off, once the error is handled. What's worse,
in the code that throws, you have no idea where you're jumping to.
When you return an error code, you are fulfilling a contract. When
you throw, you're saying, "I know I was talking to you, but I'm going
to jump over you now, and talk to your boss instead." It's rude. If
it's not an emergency, don't do that; if it is an emergency, then we
should crash.
C programs that test the return value against 0 are much easier to
follow than C++ programs that just soldier on as if errors can't
happen, and let their parent caller catch them.
Making try/catch/throw do something reasonable in async JavaScript
means that you need to keep multiple stacks going, which is
effectively what coros/fibers are. That's fine, I guess, but it's not
free. Translating the code into more complicated JavaScript that does
this manually is another approach, but also not free.
At least JavaScript doesn't have typed catches. Holy black mother of
darkness, that shit is intolerable. But still, nothing is as bad as
the common "On Error Resume Next" that so many terrible VB programs
start with. (A coworker of mine once remarked, "You know how you
debug VB programs? Delete 'On Error Resume Next', fix half the
problems, then put it back." I think a better response would've been
"Delete 'On Error Resume Next', polish your resume, and give your boss
2 weeks' notice.")
The really nice thing about the cb(error, result) approach is that it
constantly reminds the programmer that you *must* acknowledge that an
failure may come from any request you make. The nice thing about
.emit("error", er) throwing is that it makes unhandled errors *crash
the program* which is what they should do, and encourages you to
attach .on("error", handler) to every emitter you interact with.
In the end, in my opinion (which I am very ok with you disagreeing
with), the offensive thing about fibers and streamline is that they
encourage the use of try/catch, which blurs the line between errors
that are *mistakes* (accessing a property of null, calling .write() on
a stream after .end(), etc.), and those which are expected
application-level problems (invalid data, file missing, and so on).
On Sat, Nov 12, 2011 at 17:24, Mark Stone <mark....@gmail.com> wrote:
> Hey I just wanted to interject that as a relative newcomer I am loving
> this discussion (even if I can only follow the details of about 40% of
> it).
>
> I suspect there are no absolute right or wrong answers, but I think
> its healthy to have so vigorous a discussion, and I think it is a good
> sign for the Node community that passions are so strong. And I have a
> lot of confidence in Tim; anything he works on will likely be a net
> gain for Node. People can make their own decisions about whether or
> not to make use of his contributions.
>
> I came to Node because JavaScript is something I've dabbled in for a
> long time, and the notion of server side JavaScript was appealing.
> Having done some reading before I plunged in, I also felt that
> architecturally Node was a much better approach to web serving and web
> services than, say, PHP, at least for the sort of code I was
> interested in writing. I have stayed with Node, and continue to
> struggle to wrap my head around it, because I have become a real
> believer in functional programming.
>
> Getting from "believer" to "adept practitioner" has been a real
> struggle for me. I learned programming in an imperative world; my
> first languages were Algol and Fortran, and my first big project in
> college was to write an Algol compiler for the PDP-1170. Object-
> oriented programming came along after I had stopped taking programming
> classes, and something about it always struck me as awkward. Not just
> hard to learn, but philosophically awkward. Functional programming is
> even harder to learn, and I don't know if I will ever completely get
> there, but it is elegant. Flat out elegant, no question.
>
> As a beginner aspiring to code Node the right way and use it in the
> ways that it is well suited, I don't want a bunch of synchronous
> crutches and workarounds. I want Node, Node modules, and Node examples
> to nudge me towards doing things the correct way. We beginners need
> tools to do what we want, but we also need to be broken of bad habits.
> Don't encourage us to continue those bad habits, and don't be shy
> about telling us off when we're being idiots. Just because JavaScript
> is easy and available, don't let us derail your community or take Node
> in a direction it shouldn't be going in.
>
> You guys all rock. Keep up the good discussion and the good work.
>
> -Mark
> --
> Mark Stone || mark....@gmail.com || 253-223-2159 || Technical
> Project Manager, Adxstudio
> Co-author and Editor, "Open Sources", "Open Sources 2.0"
> Alumnus: VA Linux systems, Wizards of the Coast, Microsoft (Server &
> Tools Business)
>
> --
> Job Board: http://jobs.nodejs.org/
> Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
> You received this message because you are subscribed to the Google
> Groups "nodejs" group.
> To post to this group, send email to nod...@googlegroups.com
> To unsubscribe from this group, send email to
> nodejs+un...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/nodejs?hl=en?hl=en
>
+1 for being a good read.
+1 for not using try/catch
No, but the second is broken. JSON can hold null as well, so you do
not destinguish between "null" and "Invalide { JSON" input. As simple
as possible, but not simpler! Throwing errors is in JS the resort of
function that must be able return everything. Its because Javascript
contary to other script languages has no multiple return values.
Parsing the string twice, first for check then for get is double as
CPU expensive just for some coding taste? Creating an object/array is
also not elegent and does not come free for the GC as well.
For this regard I like the java approach on exceptions, typed catches,
and your function either must catch it, or must write in the
declaration that it might throw it (and thus passes through)
The former one actually harder as more characters, its just your
coding style that blows it up.
try { foo = JSON.parse(input) }
catch (err) { return "invalid data" }
This one might not be broken, if it would work, but alas JS can't:
err, foo = JSON.parse(input)
if (!err) return "invalid data"
Even when written inline, the try/catch is doing a lot more work than
it would to return a specific error-indicating value, since you can do
this:
try { foo = JSON.parse(input); bar = foo.baz; baz = bar.foo }
catch (er) { return "an error, somewhere" }
It's a goto.
> No, but the second is broken. JSON can hold null as well
Actually, since JavaScript has "undefined" as well as "null", and
"undefined" cannot be valid JSON, it could return undefined instead of
throwing. There's really no excuse there. Anyway, in practice, php's
json_decode is fine.
On Sun, Nov 13, 2011 at 11:11, vincentcr <vinc...@jucent.com> wrote:
> I'm finding that 99% of my "error handling" in node.js consists of
> sticking a "if (err) return callback(err) " or "if (err) throw err" at the
> start of almost all async invocations.
Yeah. That's how you do it.
> My opinion is that this violates the
> DRY principle, and is consequently not very robust to human error.
I find that it's expressive and clear, especially since you often want
to tack additional information onto the error to help follow it on the
way back up.
> also, it's just a convention, which even the node api does not always
> respect, because after all isn't it non-sensical to have an err param if
> your function cannot possibly return an error?
If your function cannot possibly return an error, then it's not doing
any IO, and should just return its result rather than taking a
callback.
Indeed, well you can easily write yourself a little wrapper around
JSON.parse that returns undefined. In fact now I must confess I just
remember in one app I recently actually did that :-)
> Yeah. That's how you do it.
Depends what you do. A decade back, I wrote in C a preprocessor hack
that emulated well TRY and CATCH with setjump(), longjump(); why?
Because I wrote a daemon in the tidy way, and actually you should
check every write to a filedescriptor if it failed.
if (!write(fd, foodata)) { cleanup(); return "fail"; }
if (!write(fd, bardata)) { cleanup(); return "fail"; }
if (!write(fd, bazdata)) { cleanup(); return "fail"; }
if (!write(fd, bladata)) { cleanup(); return "fail"; }
for(i = 0; i <100; i++) if (!write(fd, array[i]) [ { cleanup();
return "fail")}
Bla. Tiresome. Many people just write dirty and ignore many errors
like the return value of write (in C), but thats no excuse. That code
example might have gone with a real goto as well, but mine got into a
few subroutines etc. even this pseudo exceptions made the thing much
nicer.
Do what works for you.
>> For this regard I like the java approach on exceptions, typed catches,
>> and your function either must catch it, or must write in the
>> declaration that it might throw it (and thus passes through)
With async code, there's a muddling of issues. In sync code, a logical stack frame and a literal stack frame tend to be coupled, whereas in async code they are not. Let's say I have a series of operations I wish to perform (a logical stack frame). In a sync environment, those operations would all be contained in the same literal stack frame:a(); b(); c();However, in async code, each async call will fall into a new stack frame (or stack altogether), while still remaining within the same logical stack frame. This can be confusing and/or frustrating since as a developer I don't want to have to handle errors per literal stack frame, but per logical stack frame. The resulting model feels broken and tedious, especially to individuals coming from other environments/languages, resulting in an extra if-error-throw line per async call per stack frame. In other words, in order to perform n async operations in series, I require n EXTRA lines of code over the sync version, and that's not even including the extra if-error-throw lines per stack frame in a given async call, so the increase in total code actually ends up being something more like, n x m (where m is the average stack depth) of EXTRA LINES OF CODE just to bubble errors that in a synchronous environment are a non-issue.
Isaac & Bruno, I suspect you guys have backgrounds with very different
kinds of apps, which require different styles of error handling.
Everyone ought to preface his opinions with "in this context which I'm
familiar with, where we x & y for customers like abc, ..."
Not too many people have created a huge range of applications with
Javascript, given that it was confined to the browser and some app-
specific scripting until recently.
Ah, thanks, yes, I did not make this clear in my post.
When I say mistakes, I'm referring to stuff like SyntaxErrors, or
calling .write() after .end(). Cases where you clearly just
programmed it wrong. These are the sorts of things that smoke testing
or functional testing usually uncovers, even without disciplined unit
tests, and are a normal part of the development process.
Yes, in production, you maybe want to catch this and write a log or
page someone when it happens. (Let's not revive the whole
process.on("uncaughtException") thread.) Suffice it to say, in
development, you definitely want to crash as fast and helpfully as
possible, and in production, you may want to clean up (restart, crash,
etc.) somewhat more gracefully (perhaps after a 500 response).
When I say "errors", I'm referring to things that are outside of your
control, but reasonable. For instance, a file was deleted, a remote
server could not be reached right now, etc. These are things where,
no matter how correctly you write your code, you may still encounter a
problem, so you've got to deal with it.
I should have said, "I generally agree with Isaac."
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en