By Developers, For Developers

Historical errata for Programming Elixir 1.6

\
} end) |>

The code highlighting is messed up. Everything after and including “#{value}” is highlighted green like a comment.

PDF PgPaper PgTypeDescriptionFixed onComments
xiTYPO

Changes in the Beta…

“Mention the data and time functions added in 1.5, along with the Date::Range module.”

‘data’ -> date

2018-02-17
37TYPO

func5ion

2018-02-17
xiTYPO

“the new implicir child specifications”
-> implicit

2018-02-17
xiiTYPO

dialyxr => dialyxir?

2018-02-17
xviTYPO

looks like the link address and link text for ‘1. online Elixir code; https-colon-slash-slash-codestool-dot-coding-gnome-dot-com’ have been swapped.

2018-02-17
34TYPO

“If you are using dates and times in your code, might want to augment these”

should add “you”

2018-02-17
37TYPO

“In particular, the binding of line_no to 50”

binding to 0 instead ?

2018-02-17
38TYPO

“Here’s an example of code that isn’t in the cannonical Elixir format”

-> “canonical”

2018-02-17
48TYPO

Since the “J” in “José” is in upper case, the “d” in “dave” should also be in the same case instead of lower case.

IO.puts mr_valim.(“José”) # => Oi! José
IO.puts mr_valim.(“dave”) # => I don’t know you

2018-02-17
60TYPO

The box named “Guard Clauses vs. Conditional Logic”. We see HTML/XML entity references “>” and “<” instead of > and <.

2018-02-17
35TYPO

We’ll look at this in on page 192.

2018-02-17
xiTYPO

Last bullet point “The OTP section….”
DyanmicSupervisor should be DynamicSupervisor.

2018-02-17
xviTYPO

I just completed by second year … -> my second year

2018-02-17
299TYPO

“Wee the Task documentation” Wee => See

2018-02-18
259TYPO

In the first sentence of the section “A Little More Detail”, “…but as some one always asks” should be “…but as someone always asks” with “someone” as one word, not two.

2018-02-18
271TYPO

Footnotes 2 and 3 at the bottom of PDF page 271 are incomplete. Paragraph 2 on page 271 (beginning with “:simple_one_for_one”) is the completion of footnote 2, and the second to last paragraph on page 271 (beginning with “multiple child servers”) is the completion of footnote 3

2018-02-18
275TYPO

The first word on the page, “exists”, should be “exits”, to complete the thought from the previous page, “When the init function exits, the server is then free…”

2018-02-18
276TYPO

In the second full text paragraph on page 276, the second sentence begins “Otherwise, we call a proviate function…”, where “proviate” is a typo for “private”.

2018-02-18
292TYPO

In the last command line example before the section titled “Migrating Server State” (about 3/4 of the way down the page), the command ends with “upgrade 0.0.2”, but based on the previous examples in the section, this should be “upgrade 0.2.0” instead.

2018-02-18
295TYPO

On page 295, in the command line example for upgrading about 1/3 of the way down the page, the command ends with “upgrade 0.0.3”, but based on the rest of the example, this should be “upgrade 0.3.0” instead.

2018-02-18
53OK

Maybe it’s a typo, Idk, in with-scope.exs file, in my case it does not work with _lp. It works with plain lp.

2018-02-17OS X uses _lp; Linux lp
xviTYPO

”as an adjust professor” => as an adjunct professor?

2018-02-17
71ERROR

Third paragraph under “Heads and Tails” says

“[…] using a pipe character, The code tag should not be here.. The single […]”

presumably “The code tag should not be here” (shown in bold) is a sign of some kind of markup failure.

2018-02-17
252TYPO

“and so fee free to skip” should be “and so feel free to skip”

2018-02-18
106TYPO

In the “Stream.cycle” section, on the line where we map our infinite green/white stream into an HTML tag:

…> ~s{

#{value}

2018-02-18
156ERROR

There’s duplicate function defs in the project3/issues/lib/issues/cli.ex file, specifically these two:

decode_response({:ok, body})
process({user,project,_count})

2018-02-18
37SUGGEST

In a discussion of block scoping, a purposely chosen non-working example is used to demonstrate the Elixir philosophy of transformation through functions. I suggest also adding a working example to show how it should look in Elixir.

2018-02-17
10SUGGEST

Footnote #3 - Elixir mailing list moved in 2016. New address appears to be elixirforum dot com.

2018-02-17
3823OK

In the garbage collection section of Chapter 3. Immutability, in this part
The data in your application is divvied up between these processes, has a typo in the word divided with 2 v

2018-02-17`divvied` is an actual word... :)
33TYPO

“iex> first_half = Date.range(d1, d2)” is listed twice in the examples.

2018-02-17
38TYPO

The green header background for “basic-types/with-scope-fmt.exs” starts after “basic-”.

2018-04-06
38SUGGEST

In the example “basic-types/with-scope.exs”, I’d put a caret at the start of the regex (eg, ~r/^_lp:…), to bind it to the start of the line.

2018-02-17
39SUGGEST

In the example “basic-types/with-match.exs”, I’d put a caret at the start of the regex (eg, ~r/^xxx:…), to bind it to the start of the line.

2018-02-17
41SUGGEST

I’d change “Elixir is a functional language, therefore it is …” to “Elixir is a functional language; therefore, it is …”.

2018-02-17
44SUGGEST

I’d change “… hundreds of years of effort …” to “… hundreds of developer-years of effort …”.

2018-02-17
59SUGGEST

The function lists under the headings “Type-check functions” and “Other functions” are hard for me to read. I’d add commas and perhaps a bit more whitespace between the entries.

2018-02-17
73SUGGEST

In “How iex Displays Lists”, I’d change “… represents strings as a list of integer codepoints.” to “represents strings as lists of integer codepoints.”.

2018-02-18
77TYPO

“reduce([ head |tail ], value, fun)” is missing a space; it should be “reduce([ head | tail ], value, fun)”.

2018-02-17
83TYPO

The text “_If so, use a struct.” should be “If so, use a struct.” and be in italics. (The problem is actually a missing trailing underscore).

2018-02-17
86ERROR

The sentence ending “… and the do block prints the whole map.” is somewhat misleading. The do block returns the whole map; IO.inspect prints it.

2018-02-18
102SUGGEST

After “… if you want the sort to be stable.”, I’d add a brief explanation of stable sorts.

2018-02-18
118SUGGEST

I’d change “Heredocs are used extensively to add documentation to functions and modules.” to “Heredocs (aka here documents) are used extensively to add documentation to functions and modules. Though their form may vary, the basic idea goes back at least as far as the Unix shell.”

2018-02-18
119ERROR

“… it returns atoms, a list, or strings of characters.” should be “… it returns atoms, character lists, or strings of characters.”.

2018-02-18
124SUGGEST

In the IEEE 754 example, the transformation is incomplete, because the variable “sign” is never used.

2018-02-18
126TYPO

In the result from String.codepoints, there’s an extra space in: … “e”, "…

2018-02-18
139SUGGEST

I’d change “If the pattern match on the first line fails, …” to “If the pattern match on the first line above fails, …”.

2018-02-18
148SUGGEST

The paragraph beginning “Three of the four tests ran successfully.” mingles two kinds of presentation issues. I’d discuss the layout (eg, lhs/rhs) in one paragraph, then discuss terminal-specific (and possibly inaccessible) issues (eg, green, red) in a following paragraph.

2018-02-18
150SUGGEST

Under “Task: Use Libraries”, I’d explain that while …/elixir-lang.org/docs.html is a great starting point, …/hexdocs.pm/elixir/Kernel.html is a more direct path to library documentation.

“To prevent spam, hyperlinks are not allowed in errata”. This is a ridiculous restriction.

2018-02-18
151SUGGEST

I’d change “Next, check if any standard Erlang libraries do what you need.” to “Next, check for standard Erlang libraries that do what you need.”.

2018-02-18
151SUGGEST

I’d change “… are already available to your application.” to “… are already available for your application.”.

2018-02-18
155ERROR

There are liberties being taken with the display of the output from “Issues.GithubIssues.fetch”. Specifically, it would appear more like this: {:ok, “[\
{\\”url\\“:…”}

The reformatting is a fine idea, but I’d change the example to be something (eg, IO.puts) that actually produces the displayed result.

2018-02-18
155TYPO

“This tuple the body of the GitHub response. The first element set to :ok.” should be “This tuple is the body of the GitHub response. The first element is set to :ok.”.

2018-02-18
157SUGGEST

In “Dependencies That Aren’t in Hex”, I’d change “The dependencies you need are likely in hex, so …” to “The dependencies you need are likely to be available on hex.pm, so …”.

2018-02-18
157ERROR

“… for a successful response is a list of maps, …” should be “… for a successful response has now been converted to a list of maps, …”.

2018-02-18
160SUGGEST

The example could save a line (and processing time) by reversing the sort order and removing the first call to Enum.reverse.

2018-02-18
160SUGGEST

I’d change “… so we’ll show the listing.” to “… so we’ll just show the listing.”.

2018-02-18
161SUGGEST

As far as I can tell, the “with” construct is pointless in print_table_for_columns/2 . I’d remove it or explain why it’s there.

2018-02-18
164SUGGEST

I’d edit the earmark output so that the lines don’t fold.

2018-02-18
167SUGGEST

I’d change “The first time you run it, this will install ExDoc.” to “The first time you run this command, it will install ExDoc.”.

2018-02-18
171SUGGEST

I’d change “… the core team have spent …” to “… the core team has spent …”. (But then, I’m a yank.)

2018-02-18
172ERROR

The code display has “division::bits-16”, which is correct, but doesn’t lead to the bug that the following text describes. So, it should be changed to “division::integer-16” for the moment.

2018-02-18
172SUGGEST

I’d change “beats::15>>” to “beats::15 >>” and “beats::8>>” to “beats::8 >>”.

2018-02-18
173TYPO

“… source lines surroundingnthe breakpoint.” should be “… source lines surrounding the breakpoint.”.

2018-02-18
173TYPO

“Ah ha! ‘decode is expecting …” should be “Ah ha! decode is expecting”. (This looks like a markup error.)

2018-02-18
175SUGGEST

I’d change “beats::15>>” to “beats::15 >>” and “beats::8>>” to “beats::8 >>”.

2018-02-18
176SUGGEST

As far as I can tell, the “with” construct is pointless in print_table_for_columns/2 . I’d remove it or explain why it’s there.

2018-02-18
176SUGGEST

In the second @doc, there is a blank line following “## Example”. This is inconsistent with the format used elsewhere.

2018-02-18
180SUGGEST

When the “describe” and “test” strings are concatenated, the result should read smoothly.
This is not the case, for example, in “Stats on lists of ints calculates sum”. I’d add terminal colons to the “describe” strings, as: “Stats on lists of ints: calculates sum”

2018-02-18
183SUGGEST

When the “describe” and “propertry” strings are concatenated, the result should read smoothly. This is not the case, for example, in “Stats on lists of ints single element lists are their own sum”. I’d add terminal colons to the “describe” strings, as: “Stats on lists of ints: single element lists are their own sum”.

2018-02-18
186SUGGEST

I’d change “… by a test, the code is incomplete.” to “… by a test, the testing is incomplete.”.

2018-02-18
186SUGGEST

When explanatory text follows a heading such as “mix xref unreachable” and “mix xref warnings”, I’d like it to take the form of complete sentences. So, for example, there should be capital letters at the front, periods at the ends, etc.

2018-02-18
193TYPO

The markup is confused in “This magic is done using the mix format command. It can format single files, directory trees, and whole projects (see mix help format‘ for information).”.

Further down the page, “… looks like a dogs dinner:” should be “… looks like a dog’s dinner:”.

2018-02-18
194SUGGEST

I’d change “I find it much, …” to “I find this much, …”.

(and I’m in violent agreement with Dave on this!)

2018-02-18
195SUGGEST

Inline spacing can be used to good effect to make parallel construction more evident.

2018-02-18
207SUGGEST

The text says “The create_processes function is probably the densest piece of Elixir we’ve encountered so far.”. Indeed it is. Worse, the density is exacerbated by the fact that an anonymous function is intertwingled into the Enum.reduce call.

I generally prefer to decomplect (per Rich Hickey) the code by defining anonymous functions first, then using them by name. This makes both their definition and use simpler.

2018-02-18
208SUGGEST

I’d change “… to the last process. It will …” to “… to the last process, which will …”.

2018-02-18
210ERROR

In the example, sad_function/0 exits with a value of :boom, rather than 99 (as stated in the preceding text).

2018-02-18
219SUGGEST

I’d change “We see a dramatic reduction in elapsed time when we increase the …” to “We saw a dramatic reduction in elapsed time when we increased the …”.

2018-02-18
235TYPO

“Then exception is the …” should be “The exception is the …”.

2018-02-18
240SUGGEST

I’d change “… the server does.” to “… the server does (roughly, one function call).”.

2018-02-18
245TYPO

“… and so fee free to skip …” should be “… and so feel free to skip …”.

2018-02-18
255TYPO

“The answer depends of the …” should be “The answer depends on the …”.

2018-02-18
256TYPO

“… in the list of children and terminated, …” should be “… in the list of children are terminated, …”.

Also, the sentences should start with capital letters.

2018-04-07
262TYPO

“… to processes all the files at once.” should be “… to process all the files at once.”

“… need to cater for both big …” should be “… need to cater to both big …”.

2018-02-18
263SUGGEST

In “… when it has stopped. When it does stop, it fetches …”, the pronoun “it” is used three times. This seems awkward.

2018-02-18
265TYPO

“We want to protect the accumlating results, but that we’re …” should be “We want to protect the accumulating results, but we’re …”. (two errors)

2018-02-18
266TYPO

“… could create a poll of workers …” should be “… could create a pool of workers …”.

2018-02-18
269SUGGEST

I’d change “… is the dirwalker pid.” to “… is the dir_walker pid.”.

2018-02-18
271TYPO

The markup for both footnotes is broken, causing them to steal text from the main paragraphs.

Also, “… this can only be stragegy: one_for_one.” should be “… this can only be strategy: one_for_one.”.

2018-02-18
269SUGGEST

I’d change “as the code it is testing.” to “as the code they are testing.”.

I’d also change “trivial genserver” to “trivial GenServer”.

2018-02-18
273TYPO

“… that is has run our of work …” should be “… that it has run out of work …” (two errors)

2018-02-18
274TYPO

“Javascript” should be “JavaScript”.

“0mS” should be “0ms” (or perhaps “0 ms”)

In “When the init function …”, “init” should be in monospace font.

2018-02-18
278TYPO

“30Gb” should be “30GB” (or perhaps “30 GB”).

The phrase “duplicates in that.” is missing the rest of its sentence.

“… and so one, …” should be “… and so on, ”.

2018-02-18
287TYPO

“But how do we version the data.” should be “But how do we version the data?”.

2018-02-18
288SUGGEST

On pages 288 and 289, the vertical bars in the file tree lines containing ellipses use a different font than they do elsewhere.

2018-02-18
290TYPO

Shouldn’t “VSN" be "vsn”?

2018-02-18
291SUGGEST

I’d change “Now that’s in place, this …” to “Now that that’s in place, this …”.

2018-02-18
294TYPO

“… version number (VSN)to 1." should be "... version number (vsn) to 1.”. (two errors)

Also, it appears that the font handling needs work.

2018-02-18
297SUGGEST

I’d change “… but insulate us from these details.” to “… but insulate us from low-level details.”.

2018-02-18
310SUGGEST

I’d change “… to ensure the named module is …” to “… to ensure that the named module is …”.

2018-02-18
318SUGGEST

I’d change “Instead the macro definition …” to “Instead, the macro definition …”.

I’d also change “… distinct to the scope …” to “… distinct from the scope …”.

2018-02-18
320SUGGEST

I’d change “… we rewrite the …” to “… we can rewrite the …”.

OTOH, the word “can” is being rather heavily used here, so I might just rewrite things a bit..

2018-04-07
324ERROR

The book says “… see Appendix 2, Type Specifications and Type Checking.” I’d love to, if it were present…

2018-04-07
332SUGGEST

I’d change “… what we hard-core folks call strings).” to “… what we hard-core folks call a string).”.

2018-02-18
337SUGGEST

I’d call the module “MIDI”, rather than “Midi”.

2018-02-18
339SUGGEST

I’d change “Here’s the versions …” to “Here are the versions …”.

“The next two function heads to the actual iteration.” should be “The next two function heads do the actual iteration.”.

I’d change “… in the documentation you’ll recognize …” to “… in the documentation, you’ll recognize …”.

I’d change “Instead of doing list …” to “However, instead of doing list …”.

Finally, the name of the standard is “MIDI”, not “midi” (as used several times in the text).

2018-02-18
341TYPO

“… I happen to know what we won’t be …” should be “… I happen to know that we won’t be …”.

“… got back 2 Midi.Frame structures.” should be “… got back two Midi.Frame structures.”.

I’d change “… it is being read piece at a time …” to “… it is being read one piece at a time …”.

2018-02-18
348SUGGEST

I’d change “beats::15>>)” to “beats::15 >>)” and “beats::8>>)” to “beats::8 >>)”.

2018-02-18
356SUGGEST

I’d change “Verify it builds …” to “Verify that it builds …”.

2018-02-18
285ERROR

Sequence.Supervisor.start_link() is undefined, meanwhile module Sequence.Supervisor has nowhere been introduced.

Probably relating to “mismatched code” of page 283 ?

2018-02-18
268ERROR

This page adds Duper.PathFinder, Duper.WorkerSupervisor, and Duper.Gatherer to application.ex, which causes the “mix test” on the next page to fail because they don’t exist yet.

2018-02-18
271TYPO

“Let’s create the supervisor (in lib/worker_supervisor.ex)” has the wrong path; it should be lib/duper/worker_supervisor.ex

2018-02-18
61TYPO

Appears to be a stray “IO” between the mm/default_params1.exs code and “If you compile this”…

2018-02-17
266TYPO

“has on elasped time” -> elapsed

2018-02-18
264TYPO

“Notice that we have a mixture of synchronous messaging (the double-headed arrows) and”

Mistake on schema?
I do not see double-headed arrows.

2018-02-18
273TYPO

“gatherer that is has run our of work” -> should be “it has”

2018-02-18
9OK

In section “Compile and Run” : “tired” instead of “tire” (See below)
“Once you tire of writing one-line programs in iex….”

2018-02-17I think it is correct as is. \n \n
186175TYPO

Instead of ‘You can start by getting help for IEx.break/4’ use
‘You can start by getting help for IEx.break!/4’ (note the exclamation mark)

2018-02-18
320ERROR

The book says that the Macro module has two functions to list the unary and binary operators. It seems that Elixir 1.6.1 no longer has those. Elixir 1.5.3 did.

Elixir 1.6.1:
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Interactive Elixir (1.6.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> require Macro
Macro
iex(2)> Macro.binary_ops

(UndefinedFunctionError) function Macro.binary_ops/0 is undefined or private
(elixir) Macro.binary_ops()
iex(2)> Macro.unary_ops

(UndefinedFunctionError) function Macro.unary_ops/0 is undefined or private
(elixir) Macro.unary_ops()
iex(2)>

Book:

iex> require Macro
nil
iex> Macro.binary_ops
[:=, :!, :==, :!=, :<=, :>=, :&&, :||, :<>, :, :—, :\\\\, :::, :<-, :..,
:|>, :=~, :<, :>, :, :+, :, :*, :/, :=, :|, :., :and, :or, :when, :in,
:>, :<<, :, :<, :<, :<|>, :<<<, :>>>, :|||, :&&&, :^, :] iex> Macro.unary_ops
[:!, :@, :^, :not, :+, :-, :~, :&]

2018-02-18
7SUGGEST

the information displayed when using the i helper is missing the Implemented protocols section (1.6.1)

2018-02-17
259TYPO

In ‘Going up one more level, when you use GenServer in a server module, Elixir will
define a default check_spec/2 function in that module.’, instead of ‘check_spec/2’ use
‘child_spec/1’.

See the Elixir source code for GenServer, line 66 (sorry, can not hyperlink due to spam prevention)

2018-02-18
263TYPO

In ‘to maximize our use of the CPU and IO bandwodth’, change to ‘bandwidth’

2018-02-18
298TYPO

In ‘Supervisor.start_Link(children,strategy::one_for_one)’ change ‘start_Link’ to ‘start_link’

2018-02-18
288TYPO

To deploy it, we’d basically copy it just as we did before, the stop
the old app and start the new one.

s/the stop/then stop/

2018-04-07
3837TYPO

“For that reason, you’ll a warning:”

should probably read

“For that reason, you’ll get a warning:”

2018-04-06
152SUGGEST

We’re adding httpoison v0.13.0 lib to the issues project. The lates (stable?_) version is actually 1.0.0

2018-04-06
153SUGGEST

I’d love to see what’s your recommendation for testing such module as Issues.GithubIssues. In particular, how do we mock HTTPoison.get responses.

2018-04-06Split the fetching from the processing, and you can feed mock responses directly to the processing.
257ERROR

“Elixir will define a default check_spec function” should be “Elixir will define a default child_spec/1 function”

2018-04-07
257ERROR

Supervisor.child_spec has arity 2, not 1. Error in sentence: “You can create a child spec map using the Supervisor.child_spec/1 function.”

2018-04-07
269TYPO

“which it turn” should be “which in turn”

2018-04-07
273SUGGEST

When a worker is done, like in the add_result(nil) case, it should return {:stop, :normal, nil} instead of {:noreply, nil}. This will stop the process and it won’t restart since it is marked as :transient.

2018-04-07
210ERROR

Can not open the link Exercise: WorkingWithMultipleProcesses-2
( forums.pragprog.com/forums/322/topics/11959). The error shows “We’re sorry, but something went wrong.”

231223OK

iex(node_one@light-boy)> Node.spawn(:“node_one@light-boy”, func)
#PID<0.57.0>
node_one@light-boy

My machine shows node name first and pid.

elixir -v
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.6.1 (compiled with OTP 20)

2018-04-07Can you think why that might be? Try doing the same thing 10 times?
84SUGGEST

It isn’t obvious why the code uses the access operator for options[:fg], but not for :bg or :font. A bit more explanation might be helpful.

2018-04-06Just to show options...
93ERROR

The purported output for maps/access1.exs contains the strings “Ewes” and “EWES”. To match the map definition above, these should be “Elwes” and “ELWES”.

2018-04-06
119ERROR

At the end of the page, the description of the ~W and ~w sigils is “… whether it returns atoms, a list, or strings of characters.”. These sigils always return a list; the type specifier determines the encoding of the list elements.

So, the text should read “… it returns lists of atoms, character lists, or strings of characters.”.

2018-04-06
155TYPO

“The first element set to :ok.” should be “The first element is set to :ok.”.

2018-04-06
160SUGGEST

The …/table_formatter.ex listing uses the “with” expression. On pp. 37-38, a couple of reasons are given for using “with”. One is pattern matching, which isn’t being used. The other is scope control, which isn’t needed. So, why does this code use “with”?

Note that this same question applies to the code on page 174.

2018-04-06'cos I like the way it looks :)
162SUGGEST

There are certainly benefits to using escript (e.g., dependency management, startup latency, use of mix), but there is also a substantial cost in terms of storage space, convenience of integration with the command line, etc.

It might be worthwhile to discuss (whether and) how one can use Elixir as a scripting language.

2018-04-06
170SUGGEST

I’d change “three 16 bit fields” to “three 16-bit fields”.

2018-04-06
38ERROR

with-scope.exs does not match for me with change introduced by #82566 (leading ^ in regex). Version in 1.3 book works fine. I think the “m” option might be needed for regex here?

2018-04-06
171TYPO

The “iex -S mix” output shows three blank lines (9, 10, 12). Only one of these appears in the corresponding code (shown above.)

2018-04-06
171TYPO

In “The value in division … to decode.”, “decode” should be “decode/1”. Also, “division” and “decode/1” should be printed in code font.

2018-04-06
181SUGGEST

There is no explanation for the use of "tag :two" in the code listings for stats_property_test.exs. Is this related to the use of context[:two] in s setup callback (not shown)? I'd either explain or eliminate these tag lines.

2018-04-07
191TYPO

“dogs dinner” should be “dog’s dinner”.

2018-04-07
193TYPO

“code to an project” should be “code to a project”

2018-04-07
206SUGGEST

The text says “All this is done on line 14.”, but it’s not clear what “All this” refers to, given that the preceding paragraph is about the definition of code_to_run.
I’d change this line to read something like “Most of the real work in create_processes/1 is done in line 14.”.

2018-04-07
206SUGGEST

The sentence starting “It will increment it …” uses “it” to refer to two different things (the process and the initial value). Later in the paragraph, “it” is used to refer to a third thing (the computed result). This seems awkward and needlessly confusing.

2018-04-07
217SUGGEST

The paragraph starting “Cody Russell” starts in past tense (ran), changes to present tense (see, increase), then changes back to past tense (hit). I’d use the same tense throughout.

2018-04-07
243TYPO

“a free standing chunk” should be “a free-standing chunk”

2018-04-07
260TYPO

“We definely will” should be “We definitely will”

2018-04-07
263TYPO

“the accumlating results” should be “the accumulating results”

2018-04-07
267TYPO

“trivial genserver” should be “trivial GenServer”

2018-04-07
335TYPO

On pages 335, 337, 338, and 339, “midi file” should be MIDI file.

2018-04-07
356SUGGEST

The text above the mix.exs code says “… our mix.exs gets a little more complicated”,
but I don’t see anything in the code that relates to adding the LineSigil dependency. This should be fixed and/or explained.

111TYPO

“iex> Enum.into 1..5, [100, 101 ]” should be “iex> Enum.into 1..5, [100, 101]” (remove space after 101)

2018-04-06
38ERROR

In this line of code:
[_, uid, gid] = Regex.run(~r/^_lp:.*?:(\\d):(\\d)/, content)
we need to remove the caret symbol (^)

As when you run the code it throw this error:
Compilation error in file with-scope.exs

(MatchError) no match of right hand side value: nil
with-scope.exs:16: (file)
(elixir) lib/kernel/parallel_compiler.ex:198: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

(CompileError) compile error
(iex) lib/iex/helpers.ex:183: IEx.Helpers.c/2

But when you remove that symbol, code runs successfully and return the expected value.

2018-04-06
222TYPO

Inside “If You Run OS X , second sentence: ”If the aren’t enabled," -> “If they aren’t enabled”

2018-04-07
246TYPO

Paragraph 3, last sentence: “no need to run in inside a server for that.” -> “it”

2018-04-07
76TYPO

linked exercise text in not formatted and is very difficult to understand

Hints:

You may need to implement helper functions with an additional parameter (the currently guessed number).
the div(a,b) function performs integer division
guard clauses are your friends
patterns can match the low and high parts of a range (a..b=4..8) === {::comment} defmodule Chop do def guess(actual, range = low..high) do guess = div(low+high, 2) IO.puts “Is it #{guess}” guess(actual, guess, range) end
def guess(actual, actual, _), do: actual def guess(actual, guess, _low..high) when guess < actual, do: guess(actual, guess+1..high) def guess(actual, guess, low.._high) when guess > actual, do: guess(actual, low..guess-1) end {:/comment}

2018-04-06Strange: it looks of to me...
9TYPO

“Once you tire of” should probably be “Once you are tired of”.

2018-04-06https://www.merriam-webster.com/dictionary/tire%20of
10TYPO

Footnote 2 points to “…/elixir13”, not sure whether it should point to “…/elixir16”

2018-04-06
11TYPO

Footnote 4 points to the forum of the previous version.

2018-04-06
33SUGGEST

Sigils, i.e. for Date are employed on this page, without being introduced, yet. Maybe a few words will help newcomers.

2018-04-06
113TYPO

There is a type in function call.

On page 113 pdf, where Bullet Point == Select elements by position or criteria:

In the last example there is a typo:

iex> Enum.reject(list, &Integer.is_even/1)
[1, 3, 5]

I believe the correct function call is:
iex> Enum.filter(list, &Integer.is_odd/1)
[1, 3, 5]

2018-04-06Reject is fine here. It illustrates an additional function.
44TYPO

“there are hundreds of years of effort in there just waiting for you to use.”, probably “hundreds of years” must be “decades”

2018-04-06No, it's easily hundreds of years of effort, even though it is decades of elapsed time.
257TYPO

There’s a reference to `check_spec` which should probably be `child_spec`.

2018-04-07
250SUGGEST

One of the sentences is “Copy the Sequence.Server module from the last chapter”.
In the last chapter, two versions of the server were introduced: first, the standard one was presented; next, the “heretical” version with separate API, implementation and server was also presented (p. 244).

The reference to the “module from the last chapter” is thus ambiguous. It’s not a big deal, as the rest of the chapter makes it clear which version is being used, but it would help to have add a note to make explicit.

2018-04-07
260TYPO

“for example what sequence things happen it” should probably be “for example what sequence things happen IN”.

2018-04-07
266SUGGEST

In the code snippet that refers to application.ex, the line that says “Duper.Results,” should be highlighted, as it’s effectively the only part of that file that needs to change (everything else stays as it was created by mix).

2018-04-07
267SUGGEST

I may be wrong, but I think that adding dir_walker to mix.exs won’t be enough: it will be necessary to run “mix deps.get” before the dependency is available.
Without getting the dependency, “mix run” on page 275 will fail, due to “Unchecked dependencies for environment dev”.

2018-04-07
93TYPO

The application of String.upcase/1 on “Elwes” should be “ELWES”, not “EWES”.

2018-04-06
100TYPO

The outcome of iex> require Integer is not “nil” but “Integer”.

2018-04-06
1661ERROR

The .mobi version has an issue at location 1564 that is not present in the PDF — spurious extra lines and colons in the code listing . I can’t cut and paste it in a way that illustrates the problem.

38ERROR

Thanks for writting the book Dave and all you do! :-)

A small error/typo on page 38: Regexp ~r/^_lp:.*?:(\\d):(\\d)/ should have “m” modifier at the end or it returns always nil. So the line should read:

Regex.run(~r/^lp:.*?:(\\d):(\\d)/m, content) do

Additionally when I have read the example it really felt quite awkward and far from what a real use-case for getting uid and gid would be. One probably would like to have gid and uid returned from the with block to do some sort of comparison/safe-guard like checking if the group is what it should be and whether the uid is greater or equal 1000 or something like this.
I understood why you would return an interpolated string after reading the next example but it was one of the first more-real piece of code in the book and it looked really strange.

The more less-awkward code would probably look more like this:

content = “Now is the time”
[_, uid, gid] = with {:ok, file} = File.open(“/etc/passwd”),
content = IO.read(file, :all),
:ok = File.close(file) do
Regex.run(~r/^lp:.*?:(\\d):(\\d)/m, content)
end

IO.puts “Group: #{gid}, User: #{uid}”
IO.puts content

but then it doesn’t yeld itself to show the point you are making next so I’m not sure if anything could be done with it. Just wanted to pass the felling I got when reading the example.

2018-04-06
86SUGGEST

The terms ‘comprehension’ and ‘generator’ are used on this page for the first time in the book, without taking a pause to explain them or point to material later in the book. I’d suggest either deferring use of the terminology or taking a moment to define it.

2018-04-06The explanation is just before the code.
222TYPO

In the .mobi format, at the equivalent point to PDF page 222, the stacked vertical windows show up in a very narrow (roughly 1") column.

167SUGGEST

In the screenshot of the TableFormatter documentation you’re showing elements which are not added until page 175. Maybe you could add a note saying something like, “Your version is plainer than this… You’ll fix that in the Testing section which follows.”

167TYPO

This is a really minor one but the output from invoking ‘mix docs’ is different for me (using Elixir 1.6.4, ex_doc 0.18.3 and earmark 1.2.5).

I get:

Docs successfully generated.
View them at “doc/index.html”.

181TYPO

In the section at the bottom of the page where you break the test you change it to ’
check all number <- real do’. This should be ‘check all number <- integer()l do’.

It’s also not brilliantly clear what is the new code and what the output from the failed test is.

184SUGGEST

The testing code in Structuring Tests (starting on page 177) is done in the pbt project rather than the issues project. It wasn’t too jarring at the start of the section but you jump back to using the issues project for test coverage and it took me a few seconds to realise. It might be worth either moving all of the Structuring Tests code into the issues project (my preference) or stating that you’re switching back to issues.

Further, in the mix.exs file in issues in the Test Coverage section you’ve added ‘override: true’ to the earmark dependency. It might be worth explaining why.

156ERROR

In some parts it says to use “mix deps.get” to install the dependencies, after adding in “mix.exs”. However, it will only be possible to use “mix deps.get”.

As the mix says:

  • poison (Hex package)
       the dependency is not available, run “mix deps.get”

With:
Elixir 1.6.4
Mix 1.6.4

37TYPO

“However, Elixir thinks that this is a risk way to write code.”

“risk” should be changed to “risky.”

206SUGGEST

The github issue (1050) referred to in the footnote of this page has now been closed.

267ERROR

The code for path_finder.ex has the line ‘me PathFinder'. Instead it should be 'me MODULE’. Otherwise it throws an error when trying to start the application via iex -S mix since it cannot find PathFiner..

21SUGGEST

I think “At your very core, you believe that 99 will always have the value 99.”
should be “At your very core, you believe that count will always have the value 99.”

37TYPO

“However, Elixir thinks that this is a risk way to write code.” I think “risk” should be changed to “risky”

ivTYPO

On page iv, line 3 of text, “I just completed by second year …” should read “I just completed my second year …”

191TYPO

The code before formatted is the same code after formatted in both examples: “no_vowels string” and “@names”. I’ve checked in Beta 2 and it is OK, but in the Beta 3 it is not.

192TYPO

The code before formatted is the same code after formatted in the trailing comma example. I’ve checked in Beta 2 and it is OK, but in the Beta 3 it is not.

178SUGGEST

In this discussion of using the setup block in ExUnit, it seems odd that you could return a keyword list from the setup block and then access it using `fixture.list` syntax. But that is what is described here (on page 179 in the second paragraph)…

> This data is passed to our tests as a second parameter, following the test name. In my tests, I’ve called this parameter fixture. I then access the individual fields using the fixture.list syntax.

After studying Keyword Lists, that doesn’t sound right. You have to actually reference the ExUnit documentation in the Callbacks module to discover the magic of returning a keyword list, map, or `{:ok, keyword | map}` tuple from the setup block which merges that list into the Context which is what gets passed to the test macro. That Context is a map (which you can obviously access using the `fixture.list` syntax.

My suggestion would be to add a paragraph explaining that magic since most of Elixir is more explicit than that. So I think most readers will be in a “everything is more explicit in Elixir” mindset and might miss that.

```
defmodule TestStats1 do use ExUnit.Case
describe “Stats on lists of ints” do
# Returning a Keyword List or a Map from the setup block will merge that data structure into
# a map called the Context, which will be available in the test block below. I’ve called my context
# `fixture`.
setup do
[ list: [1, 3, 5, 7, 9, 11],
sum: 36,
count: 6
]
end

test “calculates sum”, fixture do
assert Stats.sum(fixture.list) == fixture.sum
end

…omitted for brevity…
end
end
```

It seems like it might be a bit much to explain in a comment as I attempted above, so perhaps a paragraph would be the best option.

160TYPO

Following the words “we discover the built-in Enum.take:” there’s a code excerpt.
1. There’s an arrow on the new “|> last(count)” line, but there should also be one on the function definition line “def process({user, project, count}) do” because “_count” has to be changed to “count” at this time.
2. It would also help if there was a banner like “project/3b/issues/lib/issues/cli.ex” above this example, for copying the new “last(list, count)” function.

38TYPO

in the ode listing the underscoreefore lp is missing IS:" Regex.run(~r/^lp:" SHOULD: “Regex.run(~r/^_lp:” fortunately it is correct in the following section about Code Formatting

270265SUGGEST

In duper/1/duper/lib/duper/path_finder.ex it says

@me PathFinder

while in duper/1/duper/lib/duper/results.ex it says

@me MODULE

Why not use @me MODULE in both files for consistency?

257TYPO

“Well call it Duper” is missing the apostrophe in “We’ll”

281ERROR

This section of code seems to be out of date:
defmodule Sequence do
use Application
def start(_type, _args) do
Sequence.Supervisor.start_link(Application.get_env(:sequence, :initial_number))
end
end

It refers to Sequence.Supervisor, but that wasn’t previously set up. Supervision happens in application.ex, as shown on page 252.

199TYPO

In send pid, { self, “World!” } slef should be self() to be consistent with the code sample.

92TYPO

In the example ‘maps/dynamic_nested.exs’, the value nested.westley.actor.last (“Elwes”) is called a typo by a comment. However, the example that replaces this using put_in doesn’t change the value. It seems like the original value in nested.westley.actor.last was correct, when it should have been a typo.

89TYPO

In describing the ‘swap’ function immediately above the ‘List of Lists’ section:

Text says “Given that we take two values off the list on each cycle, the initial list must have had an odd number of elements.”

Text should be “Given that we take two values off the list on each cycle, the initial list must have had an even number of elements.”

92TYPO

"
In the original body of for_location…"

Missing ‘do’ statement on line 1 of this code:

def for_location([ [ time, target_loc, temp, rain ] | tail], target_loc)
[ [ time, target_loc, temp, rain ] | for_location(tail, target_loc) ]
end

should be:
def for_location([ [ time, target_loc, temp, rain ] | tail], target_loc) do
[ [ time, target_loc, temp, rain ] | for_location(tail, target_loc) ]
end

171TYPO

There is a line that reads `iex> continue()` and it should read `pry> continue()`

174ERROR

The syntax highlighting is all off. Whatever program produced the syntax formatting for code in the PDF must not realize that the code is a continuation from page 173, and so the docstrings appear formatted as code, and the code appears formatted as docstrings. This is a common occurrence throughout the code in the book.

182TYPO

“The two new tests use a different generator: list(int) generates a number of lists, each containing zero or more ints.” list(int) should be list_of(integer()) like the code.

Also, Arithmeticerror towards the end of the page should be ArithmeticError.

298ERROR

In the source code for tasks/anagrams.exs on PDF pages 297-298, the stated goal is to load the 4 word list files in parallel using Task.async/Task.await. This is implemented in the WordlistLoader.load_from_files function on PDF page 298. The code uses Stream.map to build a lazy list of Task.async calls that, in turn, initiate the loading of the specified list files. This list is then pipelined into Enum.map(&Task.await/1). The problem is that since Stream.map is used, the Task.async calls are not executed until they are referenced somehow due to lazy evaluation. They are referenced, one-by-one, by the Enum.map calling Task.await. This effectively makes the files load serially rather than in parallel. One way to fix this would be to use Enum.map instead of Stream.map.

284TYPO

“If you’re following along at home, have a look at rel.config.exs”

should be

“If you’re following along at home, have a look at rel/config.exs”

The dot became a slash.

78ERROR

There is an error in the solution for the exercise at the top of the page. The exercise says to define caesar(list, n), which wraps if the character is greater than ‘z’. Here’s what the solution provide in Elixir_1.6_Exercises.pdf does:

def caesar([], _n), do: []
def caesar([head|tail], n) when head+n <= ?z do
[head+n | caesar(tail, n)]
end
def caesar([head|tail], n), do: [head+n-26 | caesar(tail, n)]

iex(57)> c “ex_listsrecursion1.exs”
warning: redefining module Lists (current version defined in memory)
ex_listsrecursion1.exs:1

[Lists]

iex(58)> Lists.caesar(‘a’, 100)
[171]

That result certainly doesn’t wrap.

78SUGGEST

My caesar(list, n) solution:

def caesar([], _n), do: []
def caesar([head|tail], n) do
[ rem(head+n-?a, 26) + ?a | caesar(tail, n) ]
end

89SUGGEST

Steven McClain’s errata report is erroneous. The sentence he reported explains the previous sentence:

This will happen [i.e. the third definition of the swap function will match] if we get to the end of the recursion and have only one element left. Given that we take two values off the list on each cycle [and we are left with one element, which is how the third function definition matches], the initial list must have had an odd number of elements.

92SUGGEST

Either get rid of the author example where you pass a function to get_in(), or explain what’s going on. For instance, Elixir calls the function with a third argument that is also a function, which is bound to the parameter variable next_fn. Where in the docs does it describe how many arguments that next_fn takes? I can see in the author example that it takes at least one argument, but what does next_fn do? How are we supposed to use it?

95SUGGEST

There are no exercises in Chapter 8. How come? Please add some exercises.

102ERROR

Exercise: ListsAndRecursion-5

The instructions say. “Implement the following Enum functions using no library functions….”, yet the solution for implementing Enum.split() uses Enum.reverse(). Unless I’m misunderstanding some technical meaning of “no library functions”, then a solution cannot use Enum.reverse(). If we are allowed to use Enum functions in our solutions, then here are my solutions:

def all?(list, func), do: Enum.all?(list, func)
def each(list, func), do: Enum.each(list, func)
etc.

Stupid.

114TYPO

In the section titled “Your Turn”, the Exercise: ListsAndRecursion-7 says:


In your last exercise of Chapter 7, Lists and Recursion, on page 71….
—-

The page number is wrong. The page number is actually page 81.

114ERROR

In the section titled “Your Turn” the Exercise:ListsAndRecursion-7 says to use a comprehension to find all the prime numbers between 2 and n. However, the provided solution does not obey those rules. A list comprehension alone cannot provide the solution, yet the directions make is sound like that is indeed the case. Instead, I believe the instructions should say something like: “Use a list comprehension in your answer”.

In addition, the provided solution is not written in Elixir 1.6. The solution may as well be written in C because no one whose first exposure to Elixir is “Programming Elixir 1.6” will have any idea what those hieroglyphics in the solution mean.

At this point, I have to comment on the sloppy editing in this book. This book’s editors should be dismissed—not thanked profusely in the forward.

160ERROR

When following “Transformation: Take First n Items” , page 160, calling last(count) method fails in compilation:

mix test
Compiling 2 files (.ex)
warning: variable “count” does not exist and is being expanded to “count()”, please use parentheses to remove the ambiguity or change the variable name
lib/issues/cli.ex:29

warning: variable “list” does not exist and is being expanded to “list()”, please use parentheses to remove the ambiguity or change the variable name
lib/issues/cli.ex:48

Compilation error in file lib/issues/cli.ex

(CompileError) lib/issues/cli.ex:29: undefined function count/0
(stdlib) lists.erl:1338: :lists.foreach/2
(stdlib) erl_eval.erl:677: :erl_eval.do_apply/6

164ERROR

Run app locally fails to have the results formatted. Instead, when running “./issues pragdave earmark 4” I’m getting the following error and results:
“”"

(Protocol.UndefinedError) protocol Enumerable not implemented for
[raw not formatted data fetched from GitHub]
" This protocol is implemented for: Date.Range, File.Stream, Function, GenEvent.Stream, HashDict, HashSet, IO.Stream, List, Map, MapSet, Range, Stream
(elixir) /private/tmp/elixir-20180727-75225-77ly1j/elixir-1.7.1/lib/elixir/lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) /private/tmp/elixir-20180727-75225-77ly1j/elixir-1.7.1/lib/elixir/lib/enum.ex:141: Enumerable.reduce/3
(elixir) lib/enum.ex:2979: Enum.sort/2
(issues) lib/issues/cli.ex:67: Issues.CLI.process/1
(elixir) lib/kernel/cli.ex:105: anonymous fn/3 in Kernel.CLI.exec_fun/2
“”"

156SUGGEST

In the first paragraph of text up from the bottom of the page, the book says,


We also have to deal with the possible error response from the fetch, so back in the CLI module we write a function….
—-

First, there is only one error response we are dealing with, so there is no “also” error. The text makes it seem like there is an additional error that we have to deal with.

Second, the error response is not from the fetch(). HTTPoison does not return a response tuple that contains :error, e.g. {:error, body}, nor does the Github APi—rather our code returns the :error tuple in handle_response(). handle_response() returns an :error tuple anytime the response from HTTPoison has a status code that is not 200, and the status code can easily be seen in the %HTTPoison.Response{} struct. Now, it is true that fetch() calls handle_response() so the :error tuple ends up being returned by handle_response() to fetch() and then fetch() returns the :error tuple, but simply saying that fetch() returns the :error tuple had me digging through the HTTPoison docs and the Github API trying to discover when either of them returned an error tuple.

I think the project is too convoluted and confusing. It’s not always best to write the most efficient code when explaining something to beginners. For instance, dividing the code up into 3 functions, just makes the code harder to follow. Writing less efficient code that is simpler to follow is always better.

Here is my suggested rewrite of the paragraph quoted at the top of this suggestion:


Now we have to deal with the possible error tuple returned by the handle_response() function, which is returned to fetch(), so back in the CLI module in the process() function where fetch() is called we write a function…
—-

Even that rewrite sounds convoluted, but at least it describes the path that the execution follows.

137SUGGEST

In the last two code examples on this page, the code does this:

dave = %{….}

case dave do
%{state: some_state} = person ->
IO.puts “#{person.name} lives in #{some_state}”

and:

dave = %{….}

case dave do
person = %{age: age} when ….
IO.puts "You are cleared to entered the Foo Bar, #{person.name}

There is no reason to introduce the person variable. The dave variable is in scope, and the person variable cannot be anyone other than dave. So, the IO.puts statements can simple be:

IO.puts “#{dave.name} lives in #{some_state}”

Or, change the name of the dave variable to person to create some suspense:

person = %{ name: “Dave”, state: “TX”, age: 27}

case person do
%{age: age} when is_number(age) and age >= 21 ->
IO.puts “You are of age, #{person.name}”
_ ->
IO.puts “Sorry, underage”
end

152ERROR

> In this case, we give the version as “~> 1.0.0”. This matches any version of HTTPoison with a major version of 1 and a minor version of 0 or greater. In IEx, type h Version for more details.

I am not sure if this is correct. I followed the book and used { :httpoison, “~> 1.0.0”} as a dependency but mix deps.get pulled in httpoison 1.0.0 and not 1.2.0.

stefan@arentz.ca

160SUGGEST

The second def from the top of the page is the definition of the function last(), which by the way is the worst name for that function that I can think of besides xYzT(). The last line of the definition of last() does this:

|> Enum.reverse

There is absolutely no reason to reverse the n most recent issues. No one in their right mind would ever suggest printing a table with the n most recent issues—then sneakily reversing their order to leave the reader of the table bewildered. What the code should present is the n most recent issues with the most recent issue being the first line in the table. Right minded people can understand that ordering.

161SUGGEST

I guess beginners to Elixir are expected to know the Erlang format sequences.

For anyone who doesn’t know Erlang, there is something that looks like a “typo” in the format_for() function:

fn width -> “~-#{width}s” end

If width is 10, then that function will create a string that looks like this:

iex(11)> width = 10
10

iex(12)> “~-#{width}s”
“~–10s”

(That’s the characters tilde, dash, 10, and s)

And in Erlang, you can format a string like this:

3> io:format(“|~–10s|~n”, [“hello”]).
|hello |
ok

(I added the pipes to show the field width. The control sequence ~n represents the operating system’s version of a newline e.g. “\
” or “\\r\
”.)

The control sequence ~s prints the argument inside the list—the second argument to io:format()— as a string (instead of a list of character codes):

5> io:format(“~s~n”, [“hello”]).
hello
ok

Compare to:

7> io:format(“~w~n”, [“hello”]).
[104,101,108,108,111]
ok

For the control sequence ~s, you can also specify the field width (right justified by default):

10> io:format(“|~10s|~n”, [“hello”]).
| hello|
ok

And, if you specify a minus sign, then the string will be left justified:

11> io:format(“|~–10s|~n”, [“hello”]).
|hello |
ok

As a result, the format_for() function on p. 161 creates a bunch of formatting control sequences for the Erlang function io:format/2, which can be accessed in Elixir by calling :io.format().

This chapter was supposed to teach us how to organize a project—not write code that could win an obfuscation contest. A project that contained some simple code, perhaps demonstrating some Elixir idioms, that depended on a few modules and some configuration would have been more instructive.

156SUGGEST

In my earlier errata for this page, I stated:


Second, the error response is not from the fetch(). HTTPoison does not return a response tuple that contains :error, e.g. {:error, body}, nor does the Github API…

That is incorrect—but the code in the book assumes that that statement is true because the definition of handle_response() on p. 156 has been reduced to a single function clause that cannot handle an error tuple returned by HTTPoison. I got the following HTTPoison error when I turned my wifi off:


  • (FunctionClauseError) no function clause matching in Issues.GithubIssues.handle_response/1

The following arguments were given to Issues.GithubIssues.handle_response/1:

# 1
{:error, %HTTPoison.Error{id: nil, reason: :nxdomain}}

Attempted function clauses (showing 1 out of 1):

def handle_response({_, %{status_code: status_code, body: body}})

(issues) lib/issues/github_issues.ex:29: Issues.GithubIssues.handle_response/1
—-

The function clause error is pretty cryptic, so I added another handle_response() def that will eventually cause the book’s code to output the HTTPoison error:

def handle_response({:error, httpoison_error}) do
body = %{
“message” => “HTTPoison error: #{httpoison_error.reason}”
}
{:error, body}
end

That additional function clause creates the necessary tuple for decode_response() to output the error message from HTTPoison.

160ERROR

At the bottom of the page there is some code:


def print_table_for_columns(rows, headers) do
with a=…
b=…
c= …
do
#Do this and that with a,b,c
end

end
——

As far as I can tell, that is a superfluous use of “with”. There would be no difference in scope for the with variables if the code was written as follows:

def print_table_for_columns(rows, headers) do
a=…
b=…
c=…

  1. Do this and that with a,b,c
    end
161ERROR

At the bottom of the page is a code listing with this line:

import ExUnit.CaptureIO #And allow us to capture stuff sent to stdout

The comment is false. We still would be able to capture stuff sent to stdout without the import statement. All the import statement does is allow us to call the functions defined in the ExUnit.CaptureIO without having to qualify them with the module name:

ExUnit.CaputureIO.some_func()

v.

some_func()

Although the book hasn’t covered macros yet, I believe the use statement:

use ExUnit.Case

performs some black magic that inserts the test functions into the current scope.

160SUGGEST

Here’s what I would consider a much simpler version of the Issues.TableFormatter module. My interface allows the user to specify both the header and width of a column:

def process_args({user, project, count}) do
Issues.GithubIssues.fetch(user, project)
|> halt_if_error (i.e. decode_response() )
|> sort_newest_first (i.e. sort_into_ascending_order() )
|> Enum.take(count)
|> TF.print_table(
[{“number”, 10}, {“created_at”, 22}, {“title”, 60}]
)
end

I also added the curtail module to my project as a dependency. The curtail module allows you to shorten text to fit into a specified column width, which is useful to shorten the issue’s title to fit in the specified column width. The curtail module has a few quirks, and to get curtail to truncate the strings correctly, I had to specify width+3. You’ll have to play around with curtail in iex to see how it works (also check out the omission: parameter).

defmodule Issues.TableFormatter do

def print_table(issues, fields_and_widths) do
print_headers(fields_and_widths)
print_rows(issues, fields_and_widths)
:ok
end

def print_headers(fields_and_widths) do
{header1, header2} = Enum.map_reduce(fields_and_widths, [],
fn {field, width}, acc ->
{
String.pad_trailing(" #{field}“, width),
[String.duplicate(”-", width) | acc]
}
end
)

header1 |> Enum.join(“|”) |> IO.puts
header2 |> Enum.reverse |> Enum.join(“+”) |> IO.puts
end

def print_rows(issues, fields_and_widths) do
issues
|> Enum.map(
&(print_row &1, fields_and_widths)
)
end

def print_row(issue, fields_and_widths) do
Enum.map_join(fields_and_widths, “|”, fn {field, width} ->
string = " #{issue[field]}"
shortened_field = Curtail.truncate(string, length: width+3)
String.pad_trailing(shortened_field, width)
end)
|> IO.puts
end

end

The book uses keyword lists for the test data, which seems like an error to me because the issues returned from github are maps, so I used maps for my test data. Also, the book’s test data uses atoms for the keys (as required for keyword lists) and the test data doesn’t use any numbers for the values, yet the maps returned by github have strings for keys and some of the values are integers. I adjusted my test data to account for that.

Here are my tests:

defmodule TableFormatterTest do
use ExUnit.Case
import ExUnit.CaptureIO
alias Issues.TableFormatter, as: TF

@simple_test_data [
%{ “c1” => “r1 c1”, “c2” => 40, “c3” => “r1 c3”, “c4” => “r1+c4” },
%{ “c1” => “r2 c1”, “c2” => 30, “c3” => “r2 c3”, “c4” => “r2 c4” },
%{ “c1” => “r3 c1”, “c2” => 20, “c3” => “r3 c3”, “c4” => “r3 c4” },
%{ “c1” => “r4 c1”, “c2” => 10, “c3” => “r4 c3”, “c4” => “r4 c4” }
]

@fields_and_widths [
{“c1”, 10},
{“c2”, 15},
{“c4”, 12}
]

test “print_headers()” do
result = capture_io fn ->
TF.print_headers @fields_and_widths
end

assert result == “”"
c1 | c2 | c4
————————————-——————
“”"
end

test “print_row()” do
result = capture_io fn ->
List.first(simple_test_data) |> TF.print_row(fields_and_widths)
end

assert result == “”"
r1 c1 | 40 | r1+c4
“”"
end

test “print_rows()” do
result = capture_io fn ->
TF.print_rows(simple_test_data, fields_and_widths)
end

assert result == “”"
r1 c1 | 40 | r1+c4
r2 c1 | 30 | r2 c4
r3 c1 | 20 | r3 c4
r4 c1 | 10 | r4 c4
“”"
end

end

One thing you have to be careful about is getting the correct number of trailing spaces for the “assert result ==” comparisons. In my tests, the last column has a specified width of 12, so I added enough spaces at the end of each line to make the last column 12 characters wide.

289ERROR

In the code sample for “otp-app/sequence_v2/lib/sequence/server.ex”:
def terminate(_reason, current_number) do
Sequence.Stash.update(current_number)
end

But here, the “current_number” should be the state struct instead of the previous version. Hence, if only need to preserve the current_number, it should not send whole state to Stash. The code should be changed to:

def terminate(_reason, state) do
Sequence.Stash.update(state.current_number)
end

171ERROR

The command

pry> continue()

…ends up freezing iex. According to the docs:


To exit a breakpoint, the developer can either invoke continue(), which will block the shell until the next breakpoint is found or the process terminates, or invoke respawn(), which starts a new IEx shell, freeing up the pried one.
—-

I guess if there are no more breakpoints, like in the example code, the iex shell freezes, and therefore to get a new iex shell, you need to call respawn() rather than continue().

181TYPO

After the code example at the top of the page, the second sentence says:


We have to include use ExCheck at the top to include the property test framework.
—-

But there is no “include ExCheck” statement in the code.

326TYPO

The code snipet in the explanation for Exercise:LinkingModules-BehavioursAndUse-1, the function name dump_dfn should be dump_defn to be consistent with the code above.

203SUGGEST

In my opinion, this is a much clearer Chain example:

defmodule Chain do
def process(prev_pid) do
receive do
num ->
IO.puts “process ##{num} saying hi (#{inspect self()})”
send(prev_pid, num+1)
end
end

def create_processes(n) do
create_processes(self(), n)
end

def create_processes(prev_pid, 1) do
spawn(Chain, :process, [prev_pid])
end
def create_processes(prev_pid, n) do
pid = spawn(Chain, :process, [prev_pid])
create_processes(pid, n-1)
end

end

defmodule Test do
def go(n) do
last = Chain.create_processes(n)
send last, 1

receive do
n when is_integer(n) ->
“Count was: #{n}”
end

end

def timeit(n) do
:timer.tc(Test, :go, [n])
|> IO.inspect
end
end

Using the Enum.reduce() trick just servers to obfuscate the code. If you run the example above with n=10, you’ll see the output:

iex(3)> c “chain.exs”
[Test, Chain]

iex(4)> Test.go(10)
process #1 saying hi (#PID<0.108.0>)
process #2 saying hi (#PID<0.107.0>)
process #3 saying hi (#PID<0.106.0>)
process #4 saying hi (#PID<0.105.0>)
process #5 saying hi (#PID<0.104.0>)
process #6 saying hi (#PID<0.103.0>)
process #7 saying hi (#PID<0.102.0>)
process #8 saying hi (#PID<0.101.0>)
process #9 saying hi (#PID<0.100.0>)
process #10 saying hi (#PID<0.99.0>)
“Count was: 11”

The output is proof that the processes are being created. Who knows if the processes are actually being created in the book’s example?

Then, if you comment out the statement:

IO.puts “process ##{num} saying hi (#{inspect self()})”

you can run the timeit() function from the command line and see how long it takes to create n processes:

~/elixir_programs$ elixir -r chain.exs -e “Test.timeit(10)”
{1333, “Count was: 11”}

~/elixir_programs$ elixir -r chain.exs -e “Test.timeit(100)”
{1852, “Count was: 101”}

~/elixir_programs$ elixir -r chain.exs -e “Test.timeit(1_000)”
{12524, “Count was: 1001”}

~/elixir_programs$ elixir -r chain.exs -e “Test.timeit(10_000)”
{108247, “Count was: 10001”}

~/elixir_programs$ elixir -r chain.exs -e “Test.timeit(400_000)”

21:13:36.313 [error] Too many processes

(SystemLimitError) a system limit has been reached
:erlang.spawn(Chain, :process, [#PID<0.74.8>])
chain.exs:19: Chain.create_processes/2
chain.exs:28: Test.go/1
(stdlib) timer.erl:197: :timer.tc/3
chain.exs:39: Test.timeit/1
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(elixir) lib/code.ex:192: Code.eval_string/3

~/elixir_programs$ elixir —erl “+P 1000000” -r chain.exs -e “Test.timeit(400_000)”
{4011042, “Count was: 400001”}

~/elixir_programs$ elixir —erl “+P 1000000” -r chain.exs -e “Test.timeit(1_000_000)”
{9821899, “Count was: 1000001”}

If you are wondering why the count is always n+1, that also happens with the books example—it’s just that the book cheats and send()’s 0 to the last process. The code creates n processes — in addition to the main process — so there are n+1 total processes receiving a message.

81SUGGEST

The exercise description is vague enough that I was unable to understand what it was asking me to do; consider rewording this exercise to make it clearer, please.

208SUGGEST

The first line of text after the code example says,


This time we see an :EXIT message when the spawned process terminates.
—-

Well, I saw an :EXIT message when the spawned process terminated and the process WASN’T trapping exits—which is shown in the last line on p. 207.

The text should read:


This time we “receive” an :EXIT message when the spawned process terminates.
—-

But, then again, “we” aren’t receiving anything, so even better would be:


This time “the main process received” an :EXIT message when the spawned process “terminated”.
—-

168SUGGEST

The exercise at the bottom of p. 168 instructs us to parse the hourly weather data from (omitted link here), but the xml contains only one observation, so unlike in the book where we got the last N issues from github, there is no way to get the last N weather observations in xml. There is a link you can click to get a 2-day history of observations, but that is presented in html—not xml.

240ERROR

This page says “Even though we’ve recompiled the code, the old version is still running. The VM doesn’t hot-swap code until you explicitly access it by module name.”.

But for me the new code worked without having to create a new server

358TYPO

“Exceptions in Elixir are basically records”

Shouldn’t it be “Exceptions in Elixir are basically structs”?

363TYPO

In the 2nd paragraph tuple(atom,integer} should be tuple(atom,integer)
Basically a close brace (‘}’) should be a close parenthesis.

167ERROR

Trying to run ‘mix docs’ using Elixir v1.7.3 produced this error:

(RuntimeError) module Issues.CLI. was not compiled with flag —d

This appears to be due to a version incompatibility. The ExDoc docs suggest that Elixir versions 1.7 and up require ex_doc version 0.19. Updating the dependency fixes the issue:

{ :ex_doc, “~> 0.19”, only: :dev, runtime: false }

181TYPO

“We have to include use ExCheck at the top to include the property test framework.”

Seems this should be ExUnitProperties, as in the preceding code example.

284ERROR

“mix release” fails on Elixir 1.7.3 when distillery is set to the recommended version (“1.5“). Changing the dependency to ”>2.0” fixes the problem.

164ERROR

The command to run the generated `issues` escript is incorrect for windows. First, the generated file is not an executable (console) application. It is an “escript application” (as determined by the cygwin “file” command). This will not run from the commandline.

The way to execute the resulting file is:

escript issues …

99ERROR

The link in the footnote does not work. I guess you meant https :// elixir-lang .org/ docs.html ? (without the blanks)

8069TYPO

At the bottom of the page, when showing the link to Elixir documentation, the current link gives 404. It says: “4. (…)elixir-lang.org/docs/”, but should say “4. (…)elixir-lang.org/docs” or even “4. (…)elixir-lang.org/docs.html”

(I cant write the url complete, because to prevent spam, the hyperlinks are not allowed here.

179TYPO

The link to ExUnit docs is not valid and broken (returns 404).

166SUGGEST

After adding some logging to github_issues.ex, a reader is invited to play with the new code in IEx:

iex> Issues.CLI.process {“pragdave”, “earmark”, 1}

In this case, no matter where you are in your terminal (on the issues root, or in ‘../issues/lib/issues’, you will get an error:

(UndefinedFunctionError) function Issues.CLI.process/1 is undefined (module Issues.CLI is not available)
Issues.CLI.process({“pragdave”, “earmark”, 1})

What is wrong with that? Where a reader should navigate (cd) in his/her terminal to run the above command?

167ERROR

Whenever you run mix command, you will get a warning:

warning: function Poison.Parser.parse!/1 is undefined or private. Did you mean one of:

* parse!/2

lib/issues/github_issues.ex:29

I downloaded the code source and to the latest version of github_issues.ex file. Maybe the error is due to the versions of “poison” (4.0) and “httppoison” (1.5) packages I use ? Thank you

1363TYPO

In both the Kindle version of the book and the code example online (basic-types/with-scope.exs):

[_, uid, gid] = Regex.run(~r/^lp:.*?:(\\d):(\\d)/m, content)

An underscore is missing. It should be:

[_, uid, gid] = Regex.run(~r/^_lp:.*?:(\\d):(\\d)/m, content)

131TYPO

Exercise: StringsAndBinaries-7
States that the previous sales tax exercise was in “Chapter 7” on page 114. Note however that the mentioned exercise is in Chapter 10 on page 114.

354TYPO

“But if we simply run this in the apps/evaluator directort,” should be “But if we simply run this in the apps/evaluator directory,”

The keys are, like, right next to each other.

216SUGGEST

There is really no need to introduce Agent code here just to make the fibonacci code more performant.

156ERROR

Shouldn’t the “decode_response” function should be using the “defp” declaration? The same page uses “defp” for the “check_for_error” function. It does not seem like it will be used outside the module later on.

82ERROR

The code snippet to add a hashed password to each of the three users that are in the database has some errors. The code will not work unless the user also runs “alias Rumbl.Accounts.User” and the Repo.update! command is missing a parenthesis at the end.

181TYPO

After the example code tooling/pbt/test/stats_property_test.exs the narrative describing the code says “We have to include use ExCheck at the top to include the property test framework.” However the code example does not have a use ExCheck line.

99TYPO

Link to Elixir docs in the page footer is broken, gives a 404 not found.

144ERROR

Newer versions of “mix new” (e.g. 1.9.2) no longer create the “config” directory.
Also affects page numbers 157+ (section “Application Configuration”).

92TYPO

The actor’s last name - Elwes - is spelled correctly where it is meant to be spelled incorrectly. The example is meant to begin with a typo and then show it being updated to be correct, but in fact it is already correct and the update does not make any change.

325ERROR

Mix.Config is deprecated as of v1.9, in favor of Config.Reader As well, config directory is no longer automatically generated for new projects by “mix new”. Update text with instructions to to create /config directory, and populate with:

import Config
config :issues, github_url: “imagine-url-here-because-actual-urls-not-allowed.”

18SUGGEST

This applies to all of the “Your Turn” sections. Since your response to FOSTA-SESTA renders the help links like “Exercise: PatternMatching-1” useless to the reader, please consider an update simply linking to answers to the helpful questions, fulfilling their helpfulness. In this context, answers are actually the default expectation (granted, linking to a help forum is/was a cool idea). Also, thanks for the great advocacy tip — I’ll contact my senator as suggested. Cheers!

5139ERROR

The last example on page 39 doesn’t seem to work with Elixir 1.10. However, it /does/ work if you switch to using the short-form do.

iex(66)> mean = with(
…(66)> count = Enum.count(values),
…(66)> sum = Enum.sum(values)
…(66)> do
…(66)> sum / count
…(66)> end)

(CompileError) iex:70: undefined function sum/0
(stdlib 3.12.1) lists.erl:1354: :lists.mapfoldl/3
(stdlib 3.12.1) lists.erl:1354: :lists.mapfoldl/3
(stdlib 3.12.1) lists.erl:1355: :lists.mapfoldl/3

/vs/

iex(69)> mean = with(
…(69)> count = Enum.count(values),
…(69)> sum = Enum.sum(values),
…(69)> do: sum / count )
2.5

84/9584ERROR

Any link throughout the book to elixir-lang.org/docs is probably broken. In general, they can be replaced with https :// hexdocs. pm /elixir/Whatever.html (without the spaces).

112102ERROR

In the top code block on page 102 you use chunk/2, but that has since been deprecated and should be replaced with chunk_every/2.

121111ERROR

Enum.into generates a deprecation warning when used with a non-empty collection.

155ERROR

The block of JSON returned by Issues.GithubIssues.fetch(“elixir-lang”, “elixir”) should be a string instead of JSON.

In other words, like this:
{:ok,
“[{\\”url\\“:\\” et cetera

173TYPO

for IEx.break/4.

should be

for IEx.break!/4.

(with the ! )

197ERROR

In the 4th paragraph you use code styling/formatting for the words “spawn” and “receive”. You probably want to use it for the word “send” as well.

4937ERROR

The code do not work as described on Elixir 1.7+ due to a breaking change on the language. On Elixir 1.7+ it will print 50 instead of 0. The warning will not be the same as the one in the book and will depend on the specific version, but none will give the useful warning talking about shadowing, complaining only about unused variables and the such.

Reference: Issue 8076 of Elixir official repository on GitHub

PS: It is Elixir’s fault, give it broke semantic versioning, but if you do not change it, the confusion will keep spreading.

Categories: