Skip to content

Commit 09fd93f

Browse files
committed
Go over Chapter 8
1 parent 847ca34 commit 09fd93f

File tree

4 files changed

+89
-68
lines changed

4 files changed

+89
-68
lines changed

08_error.md

Lines changed: 74 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ programmers feel good to imagine them as little things that just
1717
happen to crawl into our work. In reality, of course, we put them
1818
there ourselves.
1919

20-
A program is crystallized thought. You can roughly categorize bugs
20+
If a program is crystallized thought, you can roughly categorize bugs
2121
into those caused by the thoughts being confused, and those caused by
2222
mistakes introduced while converting a thought to code. The former
2323
type is generally harder to diagnose and fix than the latter.
@@ -31,8 +31,8 @@ computer, if it knew enough about what we're trying to do. But here
3131
JavaScript's looseness is a hindrance. Its concept of bindings and
3232
properties is vague enough that it will rarely catch ((typo))s before
3333
actually running the program. And even then, it allows you to do some
34-
clearly nonsensical things without complaint, such as `true *
35-
"monkey"`.
34+
clearly nonsensical things without complaint, such as computing
35+
`true * "monkey"`.
3636

3737
{{index syntax}}
3838

@@ -41,7 +41,7 @@ program that does not follow the language's ((grammar)) will
4141
immediately make the computer complain. Other things, such as calling
4242
something that's not a function or looking up a ((property)) on an
4343
((undefined)) value, will cause an error to be reported when the
44-
program is running and encounters the action.
44+
program tries to perform the action.
4545

4646
{{index NaN, error}}
4747

@@ -85,8 +85,8 @@ with `counter` in the example, JavaScript quietly creates a global
8585
binding and uses that. In strict mode an ((error)) is reported
8686
instead. This is very helpful. It should be noted, though, that this
8787
doesn't work when the binding in question already exists as a global
88-
binding, in which case the loop will still quietly overwrite the value
89-
of that binding.
88+
binding. In that case the loop will still quietly overwrite the value
89+
of the binding.
9090

9191
{{index this, "global object", undefined, "strict mode"}}
9292

@@ -134,11 +134,11 @@ a problem even in non-strict mode.
134134
Strict mode does a few more things. It disallows giving a function
135135
multiple parameters with the same name and removes certain problematic
136136
language features entirely (such as the `with` statement, which is so
137-
problematic it is not further discussed in this book).
137+
wrong it is not further discussed in this book).
138138

139139
{{index debugging}}
140140

141-
In short, putting a `"use strict"` at the top of your program rarely
141+
In short, putting `"use strict"` at the top of your program rarely
142142
hurts and might help you spot a problem.
143143

144144
## Types
@@ -168,12 +168,13 @@ function goalOrientedRobot(state, memory) {
168168
There are a number of different conventions for annotating JavaScript
169169
programs with types.
170170

171-
What do you think would be the type of the `randomPick` function that
172-
returns a random element from an array? One thing about types is that
173-
they require their own complexity to describe some code. In this case,
174-
you'd need a _((type variable))_ _T_, which can stand in for any type,
175-
at which point you can give `randomPick` a type like `([T]) → T`
176-
(function from array of *T*s to a *T*).
171+
One thing about types is that they need to introduce their own
172+
complexity to be able to describe enough code to be useful. What do
173+
you think would be the type of the `randomPick` function that returns
174+
a random element from an array? You'd need to introduce a _((type
175+
variable))_, _T_, which can stand in for any type, so that you can
176+
give `randomPick` a type like `([T]) → T` (function from an array of
177+
*T*s to a *T*).
177178

178179
{{index "type checking", TypeScript}}
179180

@@ -198,9 +199,8 @@ we'll have to find them the hard way: by running the program and
198199
seeing whether it does the right thing.
199200

200201
Doing this by hand, again and again, is a really bad idea. Not only is
201-
it annoying, it will also necessarily be ineffective, since it takes
202-
too much time to exhaustively test everything every time you make a
203-
change.
202+
it annoying, it also tends to be ineffective, since it takes too much
203+
time to exhaustively test everything every time you make a change.
204204

205205
Computers are good at repetitive tasks, and testing is the ideal
206206
repetitive task. Automated testing is the process of writing a program
@@ -223,10 +223,10 @@ function test(label, body) {
223223
if (!body()) console.log(`Failed: ${label}`);
224224
}
225225
226-
test("convert Latin text to upper case", () => {
226+
test("convert Latin text to uppercase", () => {
227227
return "hello".toUpperCase() == "HELLO";
228228
});
229-
test("convert Greek text to upper case", () => {
229+
test("convert Greek text to uppercase", () => {
230230
return "Χαίρετε".toUpperCase() == "ΧΑΊΡΕΤΕ";
231231
});
232232
test("don't convert case-less characters", () => {
@@ -246,11 +246,10 @@ These are usually called _((test runners))_.
246246
{{index "persistent data structure"}}
247247

248248
Some code is easier to test than other code. Generally, the more
249-
external objects that it interacts with, the harder it is to set up
250-
the context in which to test it. The style of programming shown in the
251-
[previous chapter](robot), which uses self-contained persistent values
252-
rather than changing objects, tends to produce code that's easy to
253-
test.
249+
external objects that the code interacts with, the harder it is to set
250+
up the context in which to test it. The style of programming shown in
251+
the [previous chapter](robot), which uses self-contained persistent
252+
values rather than changing objects, tends be easy to test.
254253

255254
## Debugging
256255

@@ -268,10 +267,9 @@ description and that line of code, you can often see the problem.
268267

269268
But not always. Sometimes the line that triggered the problem is
270269
simply the first place where a flaky value produced elsewhere gets
271-
used in an invalid way. And sometimes there is no error message at
272-
all—just an invalid result. If you have been solving the ((exercises))
273-
in the earlier chapters, you will probably have already experienced
274-
such situations.
270+
used in an invalid way. If you have been solving the ((exercises)) in
271+
earlier chapters, you will probably have already experienced such
272+
situations.
275273

276274
{{index "decimal number", "binary number"}}
277275

@@ -357,8 +355,8 @@ whenever it reaches such a statement.
357355

358356
Not all problems can be prevented by the programmer, unfortunately. If
359357
your program communicates with the outside world in any way, it is
360-
possible to get malformed input, or to have the network fail, or to
361-
become overloaded with work.
358+
possible to get malformed input, to become overloaded with work, or to
359+
have the network fail.
362360

363361
{{index "error recovery"}}
364362

@@ -368,7 +366,7 @@ going to be used by anybody else, you usually want the program to do
368366
better than just crashing. Sometimes the right thing to do is take the
369367
bad input in stride and continue running. In other cases, it is better
370368
to report to the user what went wrong and then give up. But in either
371-
situation, the program has to actively take action in response to the
369+
situation, the program has to actively do something in response to the
372370
problem.
373371

374372
{{index "promptInteger function", validation}}
@@ -423,8 +421,8 @@ function lastElement(array) {
423421
The second issue with returning special values is that it can lead to
424422
very awkward code. If a piece of code calls `promptNumber` 10 times,
425423
it has to check 10 times whether `null` was returned. And if its
426-
response to finding `null` is to simply return `null` itself, the
427-
caller will in turn have to check for it, and so on.
424+
response to finding `null` is to simply return `null` itself, callers
425+
of the function will in turn have to check for it, and so on.
428426

429427
## Exceptions
430428

@@ -436,7 +434,7 @@ how to handle the problem. This is what _((exception handling))_ does.
436434

437435
{{index "control flow", "raising (exception)", "throw keyword", "call stack"}}
438436

439-
Exceptions are a mechanism that make it possible for code that runs
437+
Exceptions are a mechanism that makes it possible for code that runs
440438
into a problem to _raise_ (or _throw_) an exception. An exception can
441439
be any value. Raising one somewhat resembles a super-charged return
442440
from a function: it jumps out of not just the current function but
@@ -467,10 +465,11 @@ function promptDirection(question) {
467465
}
468466
469467
function look() {
470-
if (promptDirection("Which way?") == "L")
468+
if (promptDirection("Which way?") == "L") {
471469
return "a house";
472-
else
470+
} else {
473471
return "two angry bears";
472+
}
474473
}
475474
476475
try {
@@ -504,7 +503,7 @@ the failing call.
504503

505504
{{index "exception handling"}}
506505

507-
Note that the function `look` completely ignores the possibility that
506+
Note that the `look` function completely ignores the possibility that
508507
`promptDirection` might go wrong. This is the big advantage of
509508
exceptions—error-handling code is necessary only at the point where
510509
the error occurs and at the point where it is handled. The functions
@@ -517,9 +516,9 @@ Well, almost...
517516
{{index "exception handling", "cleaning up"}}
518517

519518
The effect of an exception is another kind of ((control flow)). Every
520-
action that might cause an exception, which is at least every function
521-
call and property access, might cause control to suddenly leave your
522-
code.
519+
action that might cause an exception, which is pretty much every
520+
function call and property access, might cause control to suddenly
521+
leave your code.
523522

524523
That means that when code has several side effects, even if its
525524
"regular" control flow looks like they'll always all happen, an
@@ -530,7 +529,7 @@ exception might prevent some of them from taking place.
530529
Here is some really bad banking code.
531530

532531
```{includeCode: true}
533-
let accounts = {
532+
const accounts = {
534533
a: 100,
535534
b: 0,
536535
c: 20
@@ -577,9 +576,8 @@ there is no problem.
577576
But that isn't always practical. So there is another feature that
578577
`try` statements have. They may be followed by a `finally` block
579578
either instead of or in addition to a `catch` block. A `finally` block
580-
means "No matter _what_ happens, run this code after trying to run the
581-
code in the `try` block". If a function has to clean something up, the
582-
cleanup code should usually be put into a `finally` block.
579+
says "no matter _what_ happens, run this code after trying to run the
580+
code in the `try` block".
583581

584582
```{includeCode: true}
585583
function transfer(from, amount) {
@@ -639,7 +637,7 @@ were on the stack when the problem occurred.
639637
{{index "user interface"}}
640638

641639
For problems that are _expected_ to happen during routine use,
642-
crashing with an unhandled exception is not a very friendly response.
640+
crashing with an unhandled exception is a terrible strategy.
643641

644642
{{index syntax, [function, application], "exception handling", "Error type"}}
645643

@@ -701,14 +699,14 @@ carefully about how you might be hiding information.
701699

702700
So we want to catch a _specific_ kind of exception. We can do this by
703701
checking in the `catch` block whether the exception we got is the one
704-
we are interested in and by rethrowing it otherwise. But how do we
702+
we are interested in and rethrowing it otherwise. But how do we
705703
recognize an exception?
706704

707-
Of course, we could compare its `message` property against the
708-
((error)) message we happen to expect. But that's a shaky way to write
709-
code—we'd be using information that's intended for human consumption
710-
(the message) to make a programmatic decision. As soon as someone
711-
changes (or translates) the message, the code will stop working.
705+
We could compare its `message` property against the ((error)) message
706+
we happen to expect. But that's a shaky way to write code—we'd be
707+
using information that's intended for human consumption (the message)
708+
to make a programmatic decision. As soon as someone changes (or
709+
translates) the message, the code will stop working.
712710

713711
{{index "Error type", "instanceof operator", "promptDirection function"}}
714712

@@ -790,7 +788,7 @@ for such mistakes to go unnoticed, and easier to find their cause when
790788
they occur.
791789

792790
I do not recommend trying to write assertions for every possible kind
793-
of bad input. That'd be a lot of work, and would lead to very noisy
791+
of bad input. That'd be a lot of work and would lead to very noisy
794792
code. You'll want to reserve them for mistakes that are easy to make
795793
(or that you find yourself making).
796794

@@ -804,15 +802,15 @@ assertions to your programs.
804802
Problems caused by factors outside the program's control should
805803
usually be handled gracefully. Sometimes, when the problem can be
806804
handled locally, special return values are a good way to track them.
807-
Otherwise, exceptions are preferable.
805+
Otherwise, exceptions may be preferable.
808806

809807
Throwing an exception causes the call stack to be unwound until the
810808
next enclosing `try/catch` block or until the bottom of the stack. The
811809
exception value will be given to the `catch` block that catches it,
812810
which should verify that it is actually the expected kind of exception
813811
and then do something with it. To help address the unpredictable
814812
control flow caused by exceptions, `finally` blocks can be used to
815-
ensure a piece of code is _always_ run when a block finishes.
813+
ensure a piece of code _always_ runs when a block finishes.
816814

817815
## Exercises
818816

@@ -836,10 +834,11 @@ Make sure you handle only the exceptions you are trying to handle.
836834
class MultiplicatorUnitFailure extends Error {}
837835
838836
function primitiveMultiply(a, b) {
839-
if (Math.random() < 0.2)
837+
if (Math.random() < 0.2) {
840838
return a * b;
841-
else
839+
} else {
842840
throw new MultiplicatorUnitFailure("Klunk");
841+
}
843842
}
844843
845844
function reliableMultiply(a, b) {
@@ -874,8 +873,8 @@ hint}}
874873

875874
Consider the following (rather contrived) object:
876875

877-
```{includeCode: true}
878-
let box = {
876+
```
877+
const box = {
879878
locked: true,
880879
unlock() { this.locked = false; },
881880
lock() { this.locked = true; },
@@ -889,9 +888,9 @@ let box = {
889888

890889
{{index "private property", "access control"}}
891890

892-
It is a ((box)) with a lock. Inside is an array, but you can get at it
893-
only when the box is unlocked. Directly accessing the private
894-
`_content` property is forbidden.
891+
It is a ((box)) with a lock. There is an array in the box, but you can
892+
get at it only when the box is unlocked. Directly accessing the
893+
private `_content` property is forbidden.
895894

896895
{{index "finally keyword", "exception handling"}}
897896

@@ -903,6 +902,17 @@ argument function returned normally or threw an exception.
903902
{{if interactive
904903

905904
```
905+
const box = {
906+
locked: true,
907+
unlock() { this.locked = false; },
908+
lock() { this.locked = true; },
909+
_content: [],
910+
get content() {
911+
if (this.locked) throw new Error("Locked!");
912+
return this._content;
913+
}
914+
};
915+
906916
function withBoxUnlocked(body) {
907917
// Your code here.
908918
}
@@ -931,10 +941,9 @@ if}}
931941

932942
{{index "locked box (exercise)", "finally keyword", "try keyword"}}
933943

934-
This exercise calls for a `finally` block, as you may have guessed.
935-
Your function should first unlock the box and then call the argument
936-
function from inside a `try` body. The `finally` block after it should
937-
lock the box again.
944+
This exercise calls for a `finally` block. Your function should first
945+
unlock the box and then call the argument function from inside a `try`
946+
body. The `finally` block after it should lock the box again.
938947

939948
To make sure we don't lock the box when it wasn't already locked,
940949
check its lock at the start of the function and unlock and lock

code/solutions/08_1_retry.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
function MultiplicatorUnitFailure() {}
22

33
function primitiveMultiply(a, b) {
4-
if (Math.random() < 0.5)
4+
if (Math.random() < 0.5) {
55
return a * b;
6-
else
6+
} else {
77
throw new MultiplicatorUnitFailure();
8+
}
89
}
910

1011
function reliableMultiply(a, b) {

code/solutions/08_2_the_locked_box.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
const box = {
2+
locked: true,
3+
unlock() { this.locked = false; },
4+
lock() { this.locked = true; },
5+
_content: [],
6+
get content() {
7+
if (this.locked) throw new Error("Locked!");
8+
return this._content;
9+
}
10+
};
11+
112
function withBoxUnlocked(body) {
213
let locked = box.locked;
314
if (!locked) {

html/js/sandbox.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
run(code, output, meta) {
8282
if (output) this.output = output
8383
this.startedAt = Date.now()
84-
this.extraSecs = 2
84+
this.extraSecs = typeof code == "string" && /promtDirection/.test(code) ? 0.1 : 2
8585
this.win.__c = 0
8686
this.prepare(code)
8787
.then(code => {

0 commit comments

Comments
 (0)