By Developers, For Developers
PDF Pg | Paper Pg | Type | Description | Fixed on | Comments |
---|---|---|---|---|---|
21 | TYPO | “Those clients can all broadcast messages to each other, coordinated though the server.” s/though/through/ | 2017-04-05 | Thanks! | |
40 | TYPO | “…and add public functions to it to define it’s behavior.” s/it’s/its/ | 2017-04-05 | Thanks! | |
31 | ERROR | In the `to_string` function, the return value has an error: ``` should read def to_string(coordinate) do as `island` is not a function | 2017-04-06 | Thanks, but we do have an "island/1" function in the Coordinate module. | |
35 | TYPO | Third paragraph, last sentence: “While we’e at it” should be “While we’re at it”. | 2017-04-05 | Thanks! | |
20 | TYPO | “It’s also leads to Elixir’s great fault tolerance.” s/It’s/It/ | 2017-04-05 | Thanks! | |
21 | SUGGEST | There’s a minor inconsistency here. We used “mix new islands_engine —sup” to create the project so coordinate.ex is located in islands_engine/lib. The code headings all say agent/lib. Should I report this sort of thing? | 2017-04-05 | The agent/lib headings are for the code bundle that comes with the book, not the Elixir project on your machine. Thanks! | |
23 | SUGGEST | In listing: def island(coordinate) has already been defined to do this: This block of code should reuse that function. | 2017-04-05 | Thanks! | |
15 | SUGGEST | It may be worth mentioning when `mix new` is run that you’re calling the project “islands_engine” instead of “islands” because it’s the engine for the game. | 2017-04-07 | Thanks! | |
18 | SUGGEST | I would use either “coord” or “coordinate” variable name in all examples, but only one. It looks more consistent. | 2017-04-07 | Thanks! | |
23 | SUGGEST | I would use the previously defined function “island” in “in_island” function to avoid code duplication. def in_island?(coordinate) do or, if you prefer to use pattern matching: def in_island?(coordinate) do | 2017-04-05 | Thanks! | |
27 | ERROR | Missing “island diagram” image. Instead, I see | 2017-04-05 | Thanks! | |
28 | SUGGEST | Wouldn’t it be “IslandsEngine.Island.replace_coordinates/2” instead of “IslandsEngine.replace_coordinates/2”? | 2017-04-05 | Thanks! | |
28 | TYPO | Instead of: would be: | 2017-04-05 | Thanks! | |
38 | ERROR | You must bind the IslandSet struct to “island_set” variable, so you can access the field “atoll” later. Instead of: It should be: | 2017-04-06 | Thanks! | |
33 | SUGGEST | In order to avoid confusion, you could use “state” as the argument name in the anonymous function. It will also be consistent as you’re using “state” in other code snippets. def get_coordinate(board, key) when is_atom key do Agent.get(board, fn state -> state[key] end) | 2017-04-06 | Thanks, I try to use the name of the module/entity wherever I can, unless it is the same as a parameter passed into the function. | |
77 | ERROR | keys() function is introduced into Enum.reduce line in island_set.ex and you are asked to go into iex to test it before showing the definition of keys(). Pull keys() function definition two pages earlier. | 2017-04-07 | Thanks! | |
59 | ERROR | iex session when dealign with IslandSet doesn’t work.. {:ok, island_set} = IslandSet.start_link island_set is a pid at this point. so next line works… Agent.get(island_set, &(&1)) but very next iex line does not…. Agent.get(island_set.atoll, &(&1)) I think what you want is for previous line to reset island_set so you have a reference to struct to call .atoll on. So change it to: | 2017-04-06 | Thanks! | |
68 | TYPO | “The general form of init/1 is to patten match on an argument” misspells “pattern” as “patten” | 2017-04-06 | Thanks! | |
68 | SUGGEST | A quick explanation of what MODULE refers to would be nice here. | 2017-04-07 | Thanks! | |
72 | TYPO | Missing/broken image: | 2017-04-05 | Thanks! | |
73 | SUGGEST | “In the unlikely event that the coordinate already is a PID” — it would be nice to describe how this could happen. | 2017-04-07 | Thanks! | |
28 | ERROR | At the bottom of this page: you’re referring to “Enum.all/2” in the text but the function shown in the example is “Enum.all?/2” | 2017-04-05 | Thanks! | |
29 | SUGGEST | Why not use the short-hand function syntax in Island.forested?: Agent.get(island, &(&1)) |> Enum.all?(&Coordinate.hit?/1) | 2017-04-07 | Thanks! | |
30 | ERROR | The image at the bottom of this page does not match the description above. | 2017-04-06 | Thanks! | |
38 | ERROR | iex> {:ok, island_set} = IslandSet.start_link {:ok, #PID<0.112.0>} iex> Agent.get(island_set, &(&1)) iex> Agent.get(island_set.atoll, &(&1)) [] The 2nd function needs to rebind island_set, otherwise you get an argument error when you call island_set.atoll on the PID. | 2017-04-06 | Thanks! | |
39 | TYPO | In string_body/1 there is a semi-colon at the end of the line: island = Agent.get(island_set, &(Map.fetch!(&1, key))); | 2017-04-06 | Thanks! | |
40 | SUGGEST | You might consider combining the first two aliases to: It’s done this way earlier in the book | 2017-04-06 | Thanks! | |
57 | SUGGEST | The syntax of “{:noreply, state}” hasn’t been explained at this point, but is being used. I think there should be an explanation of why this function needs to return this value. | 2017-04-07 | Thanks! | |
29 | SUGGEST | I suggest using the following code: def state(island) do def forested?(island) do def to_string(island) do | 2017-04-07 | Thanks! | |
32 | SUGGEST | I suggest using the following code:
defp keys do defp initialized_board do | 2017-04-06 | Thanks, but in the context of the book, I like the explicitness of setting the keys and values for the board map. | |
35 | SUGGEST | I suggest using the following code: def to_string(board) do | 2017-04-07 | Thanks, but I like the elements to come out ordered. | |
15 | SUGGEST | With elixir 1.4.2 there is also a file created called: and also the next page (16) has the application.ex inside the lib/islands_engine | 2017-04-07 | Thanks! | |
27 | ERROR | Missing image for island diagram, showing: | 2017-04-05 | Thanks! | |
28 | ERROR | You say that “The anonymous function [passed as the second argument to ```Enum.all?/2] must return a boolean or nil”, but in fact it can return anything, with the result being true as long as all the collected results are all truthy (neither false nor nil). You’ve also missed the “?” from the function name in the text, though it’s correct in the code. | 2017-04-07 | Thanks! | |
26 | SUGGEST | In the IEx demonstration of Coordinate.to_string (and also later Island.to_string on p30) you show the result of both calling the function and also passing the result of calling it to IO.puts. I wonder whether using both in the same example might add a tiny bit of incidental complexity and potentially confuse some readers for a moment. | 2017-04-06 | Thanks, I'll keep watching to see if anybody reports any confusion over this. | |
30 | SUGGEST | It seems odd that when you call Island.to_string, the constituent coordinates print themselves out with “island: :none”. Presumably this missing reverse connection will be addressed later – if so, it might be worth mentioning at this point. | 2017-04-06 | Thanks! | |
38 | SUGGEST | I was confused by the statement “You may have noticed that we wrote our own function to get the struct keys …”. I’d noticed the call to “keys()”, and thought it must be some magic function auto-imported by defstruct. When I read “we wrote …” I went back and couldn’t find it – it turns out it’s actually defined further down the page. Maybe “we called” would be clearer. | 2017-04-07 | Thanks! | |
46 | TYPO | There’s an extra “the” in the second paragraph of “Wrapping up” (“instead of double lives in the both the application and the database”). | 2017-04-06 | Thanks! | |
59 | SUGGEST | It might help to explain the “%{state | test: ”new value“}}” syntax used here at the end of the page, in the handle_cast example. | 2017-04-07 | Thanks! | |
62 | SUGGEST | An explanation of what “name” is being received in init would be helpful, I think. | 2017-04-06 | Thanks! I think we explain that in the preceding paragraphs. | |
64 | TYPO | “IslandsEngine.Game” should be monospaced at the bottom of this page. | 2017-04-06 | Thanks! | |
65 | ERROR | Example for player2 here shows: %Player{:name => none, But it should be: %Player{:name => :none, (Missing a colon before none) | 2017-04-07 | Thanks! | |
65 | ERROR | Player names should be wrapped in quotes: iex> IO.puts IslandsEngine.Player.to_string(state.player1) %Player{:name => Frank, | 2017-04-07 | Thanks! | |
65 | ERROR | Player names should be wrapped in quotes: iex> IO.puts IslandsEngine.Player.to_string(state.player1) %Player{:name => Frank, | 2017-04-07 | ||
67 | SUGGEST | Agent.get(player, fn state -> state.board end) Could be written shorter: Agent.get(player, &(&1.board)) | 2017-04-06 | Thanks, but here my preference is for the more verbose form. To me, it's clearer. | |
71 | SUGGEST | While I understand that _player matches on anything else, I think it would be good to have :player2 in the second “defp opponent” call, just for consistency. | 2017-04-06 | Thanks! | |
71 | ERROR | I think this code: case Board.coordinate_hit?(opposite_board, coordinate) do Could be better written using an “if”: coordinate_hit = My rationale for this is that you’re using a “case” statement as if it were an “if” statement. The language has an “if” statement and so why not use it here? | 2017-04-06 | Thanks, but I prefer the case statement here. | |
79 | SUGGEST | I think this would be a good time to show Elixir 1.4’s Registry in action. Looks like you’re using the “old” way of doing it. | 2017-04-06 | Thanks, but it's my understanding that global registry isn't going away, and that it will even be improving in future versions of OTP. | |
36 | SUGGEST | “We know that an island set will have one each of five different island We would know this if we had played the game before. Up until this point though, this hasn’t been explained at all. Perhaps a link to somewhere which explains the rules of Islands would be handy? | 2017-04-06 | Thanks! | |
41 | ERROR | I think start_link function needs to pass name to %Player{}, otherwise pg 68 function doesn’t initialize with name “Frank” Agent.start_link(fn -> %Player{board: board, island_set: island_set, name: name} end) | 2017-04-06 | Thanks! | |
60 | TYPO | iex session example alternates between pid & game variables also, might consider using IslandsEngine.Game.call_demo instead of GenServer.call | 2017-04-06 | Thanks! | |
25 | SUGGEST | The “to_string” functions are helpful for seeing what is going on, but I didn’t really get it until I used observer to navigate through the processes to see how they are linked and look at the state. You might mention it as an option for better understanding. | 2017-04-06 | Thanks, I think this is a little early in the book for :observer. It may make an appearance later on, though. :) | |
65 | SUGGEST | The second call to Game.call_demo/1 to rebind the result to the variable state is unnecessary because the PIDs don’t change. This concept is discussed in the callout “Players are Agents” on the bottom of the previous page. Maybe deleting will cause confusion, but I think it might reinforce the concept if it is deleted. It says it’s not necessary to rebind, but then that’s what the example does. | 2017-04-06 | Thanks! | |
25 | TYPO | The implementation of Coordinate.to_string/1 calls island/1, which doesn’t exist; this should probably be in_island, which does. (Great book so far!) | 2017-04-05 | Glad you're enjoying the book! | |
25 | TYPO | (doh! Ignore that last report about island/1 - Just noticed I misnamed the function when I typed it in. Sorry!) | 2017-04-05 | No problem. :) | |
38 | ERROR | At the point when we’re told to “Open up a console session and alias IslandsEngine.IslandSet.”, we haven’t created IslandSet.keys/0, so iex doesn’t start: “undefined function keys/0”. | 2017-04-07 | Thanks! | |
39 | TYPO | When we “check the state of the atoll island”, it says to use “Agent.get(island_set.atoll, &(&1))” — I think this maybe oughta be “Agent.get(island_set, &(&1.atoll))” since island_set is just a PID. | 2017-04-06 | Thanks! | |
39 | TYPO | Oops (sorry, wish I could edit errata after submission!), when checking the state of the atoll island, calling Agent.get(island_set, &(&1.atoll)) returns the PID of the atoll island, so getting its (empty) coordinates list requires another Agent.get: “Agent.get(island_set, &(&1.atoll)) |> Agent.get(&(&1))” produces the empty list we expect. | 2017-04-06 | Thanks! | |
41 | SUGGEST | If we’re not going to set the Player’s name in its start_link function, we probably don’t need the unused “name” parameter at all. | 2017-04-06 | Thanks! | |
29 | SUGGEST | This function violates two guides from elixir style guide. “Use bare variables in the first part of a function chain” and “Avoid using the pipe operator just once”. | 2017-04-06 | Thanks! | |
29 | SUGGEST | def to_string(island) do Starts a pipeline with a function return and pipes to just on function. This is my suggestion def to_string(island) do “[” <> String.replace_leading(island_string, “, ”, “”) <> “]” | 2017-04-06 | Thanks! | |
19 | TYPO | Our job for this section is to take the IslandsEngine.Coordinate module into an take -> turn | 2017-04-05 | Thanks! | |
22 | ERROR | You might have noticed that we named the function Agent.get/3 should be You might have noticed that we called the Agent.get/3 function Nothing was named: a standard function was called. | 2017-04-05 | Thanks! | |
28, 3 | ERROR | In page 39 the line doesn’t work because island_set is a PID. My suggestion: In page 38 bind the struct to a variable island_struct = Agent.get(island_set, &(&1)) and then in page 39 Agent.get(island_struct.atoll, &(&1)) | 2017-04-06 | Thanks! | |
39 | ERROR | In page 39 the call but there are no coordinates assigned before to any island. Subsequent call to | 2017-04-06 | Thanks! | |
63 | ERROR | In page 63 this line because IslandsEngine.Player.start_link does not set the name. relevant errata #81284 | 2017-04-06 | Thanks! | |
66 | ERROR | In the middle of the page missing chart image for “the path we’ll follow”. Instead it shows the line | 2017-04-05 | Thanks! | |
66 | SUGGEST | The function def handle_call({:set_island_coordinates, player, island, coordinates}, _from, state) do Should be def handle_call({:set_island_coordinates, player, island, coordinates}, _from, state) do {:reply, :ok, state} So it would both confirm the path is state -> player and and comply with the “Elixir Style Guide” | 2017-04-07 | Thanks! | |
68 | ERROR | The function definition for set_island_coordinates/3 uses the Coordinate module but that module is not aliased in the file. The same for the linked code file. | 2017-04-07 | Thanks! | |
15 | SUGGEST | In the Preface, it would be good to add a section indicating the versions of Elixir/Phoenix used for the examples. Right now, 6 days after release of B1.0, I am already scratching my head at an inconsistency between what you say and (fresh install) what I see. | 2017-04-07 | Thanks, this will happen as I flesh out the preface. | |
70 | TYPO | The line Should be: | 2017-04-06 | Thanks! | |
70 | SUGGEST | The definition of the function def handle_call({:guess, player, coordinate}, _from, state) do looks like a good use case for the pipe operator | 2017-04-06 | Thanks! | |
71 | SUGGEST | the line is not needed in player.ex because there is already the line | 2017-04-06 | Thanks! | |
71 | SUGGEST | The variable name opposite_board in the line maybe should be opponent_boad because the board is opposite to what? | 2017-04-06 | Thanks, I went with "opponent_board" because I think it's still important to say that any guess goes toward the player's opponent's board. | |
71 | ERROR | the line (UndefinedFunctionError) function Player.to_string/1 is undefined (module Player is not available) because IslandsEngine.Player is not aliased in this iex session. Possible fix: the line | 2017-04-07 | Thanks! | |
72 | TYPO | The line | 2017-04-06 | Thanks! | |
29 | SUGGEST | I like Igor’s suggestion to use Enum.map_join in to_string/1, it’s a lot cleaner to read. Small typo in his suggestion though, first argument for Enum.map_join should be “, ” | 2017-04-07 | Thanks! | |
63 | ERROR | It looks as though IslandsEngine.Player.start_link/1 should have been updated at some point to include the starting player’s name since the name argument isn’t used, causing the player1 name to remain as :none. I’m guessing line 5 needs “name: name” added to the %Player{} struct like this: 1> def start_link(name \\\\ :none) do | 2017-04-06 | Thanks! | |
75 | SUGGEST | The function Game.forest_check/3 returns either {:miss, :none} or {:hit, key} but not {:hit, :none}. So maybe the function defp win_check({hit_or_miss, :none}, _opponent, state) do could be | 2017-04-07 | Thanks, but it {:hit, :none} is valid if the guess resulted in a hit but no islands were forested. | |
97 | SUGGEST | In the IslandsEngine project, that’s /lib/islands_engine.ex. In my installation with elixir 1.4.2 in linux that file is lib/islands_engine/application.ex also the module name is IslandsEngine.Application | 2017-04-07 | Thanks! | |
97 | TYPO | The line: | 2017-04-06 | Thanks! | |
97 | ERROR | the line That what is said in the next paragraph. Also that is where I found it in my system. | 2017-04-06 | Thanks! | |
70 | TYPO | Guard clauses should both be is_atom I think the second guard should also use is_atom (see below), not is_tuple. def guess_coordinate(pid, player, coordinate) | 2017-04-06 | Thanks! | |
80 | SUGGEST | When stopping the GenServer, why not implement by calling GenServer.stop(pid) instead of manually wiring up a cast? | 2017-04-06 | Thanks, it's just for practice, showing the form again. | |
75 | ERROR | The return value for both win_check variations should not include the :reply and state as that is already returned by the :guest handle_call function :) | 2017-04-07 | Thanks, but in the final version of those function clauses, I do believe we need them. | |
130 | SUGGEST | might consider reminding reader to recompile after adding handle_in function for “new_game” | 2017-04-07 | Thanks! | |
133 | SUGGEST | consider otherwise, the Poison.EncodeError mentioned on pg 131 would happen for any errors | 2017-04-07 | Thanks! | |
129 | SUGGEST | Might consider consistently handling the response object in the javascript functions in pages 129-133 Sometimes we console.log the entire response object, other times console.log the string within the object, e.g. response.message | 2017-04-07 | Thanks, I'll consider this. We usually use response.message if I want it to read a certain way in the console. The plain response is there so we can click on the object in the console and inspect it. | |
133 | TYPO | should be ‘game_channel.on’ not ‘game.on’ otherwise you get an error in javascript | 2017-04-06 | Thanks! | |
133 | SUGGEST | recommend IO.puts IslandsEngine.Player.to_string(game.player1) to take advantage of formatting same for player2 | 2017-04-07 | Thanks! | |
134 | SUGGEST | Consider removing iex> game = IslandsEngine.Game.call_demo({:global, “game:moon”}) Not necessary to rebind “game” variable | 2017-04-07 | Thanks! | |
134 | SUGGEST | Would it be safer to use String.to_existing_atom/1 instead of String.to_atom/1? Player, Island, and Coordinate atoms should already exist. | 2017-04-07 | Thanks, but that's not necessary in this case. We're re-using the same atoms, not creating lots of new ones. | |
118 | TYPO | File name above code box is ‘channel/lib/game_channel.ex’, but should be ‘/web/channels/game_channel.ex’. this is consistent throughout this chapter. | 2017-04-06 | Thanks, the pathname in the code box represents the path in the code bundled with the book, not code in the project on your filesystem. | |
123 | TYPO | > join(game) | 2017-04-06 | Thanks! | |
126 | TYPO | Payload map needs to have key “message” instead of “reason” to work with say_hello function definition on pg 125 pg 125 pg 126 should be: def handle_in(“hello”, payload, socket) do | 2017-04-07 | Thanks! | |
121 | SUGGEST | Consider using double quotes for consistency > var phoenix = require(‘phoenix’) The rest of the javascript code uses double quotes. Same suggestion on pg 128 | 2017-04-06 | Thanks! | |
129 | TYPO | should be ‘response.message’ not ```’response’ otherwise you get the entire payload object. The console code afterwards shows only the string returned. > game_channel.on(“said_hello”, response => { > say_hello(game_channel, “World!”) | 2017-04-07 | Thanks! | |
125 | SUGGEST | If not sure how to make it more clear and maybe it was just me, but I confused game_channel.push() on pg 125 with push/3 introduced on page 124. I kept trying to reread pg 124 to figure it out and then finally figured out my error when I got to push/3 at the top of page 127. It’s clear now when I reread it, but I got thrown off because I was looking for a push function and game_channel.push() came before push/3. | 2017-04-07 | Thanks! | |
135 | TYPO | As we have with all these new actions so far, we’ll need a new function to wrap the chanel.push call and show us what the result is. Should be channel.push | 2017-04-06 | Thanks! | |
135 | SUGGEST | Suggest explaining how to not execute the expression on the first line. If the reader presses enter, the line will be evaluated and they will not be able to use the pipe operator on the 2nd line. iex> IslandsEngine.Player.get_island_set(game.player1) | 2017-04-07 | Thanks! | |
135 | SUGGEST | Suggest reminding the reader to recompile game_channel after adding handle_in function for “set_island_coordinates”. | 2017-04-07 | Thanks, I'll consider this, but I want to avoid sounding like a broken record. :) | |
135 | TYPO | Should be game_channel not game, see update below > set_island_coordinates(game_channel, “player1”, “atoll”, [“a1”]) | 2017-04-06 | Thanks! | |
139 | SUGGEST | Consider reminding reader to recompile game_channel after adding handle_in function for “guess_coordinate” | 2017-04-07 | Thanks, same as above. | |
139 | TYPO | Function guess_coordinate has an extra coordinate that should be deleted, see below. “coordinate” is already in the params. params = {“player”: player, “coordinate”: coordinate} | 2017-04-06 | Thanks! | |
139 | TYPO | console.log(“Unable to guess a coordinates: ” + player, response) should be “coordinate”, not “coordinates” or remove “a” | 2017-04-06 | Thanks! | |
140 | ERROR | Coordinates can only keep track of the last island they’ve been set in. When we set all island coordinates to “a1” on pg 136, it creates a weird result in the IslandSet where the A1 coordinate is in the list for each island, but it only thinks it is in square: iex> IslandsEngine.Player.get_island_set(game.player1) |> …> IslandsEngine.IslandSet.to_string() |> The response object on pg 141 only returns the “square” island (you would expect it to return a list of the hit islands, but as mentioned above, the coordinate only keeps track of 1 island): > guess_coordinate(game_channel, “player2”, “a1”) undefined This seems like a minor bug. It may not need to be corrected in the code, but you may not want to highlight this as an example. | 2017-04-07 | Thanks, and yes, this is a highly unusual situation that's set up so that we can see a win without slogging through a number of guesses for each player. | |
143 | ERROR | Need to ‘alias IslandsInterface.Presence’ to use Presence functions in GameChannel | 2017-04-07 | Thanks! | |
143 | TYPO | Formatting issue with GameChannel.join/3 update. No syntax coloring and missing filename header. | 2017-04-07 | Thanks! Syntax highlighting is back. You won't see the bar above the code snippet until the we get to the final version of the function, which will be the version in the code bundle for the book. | |
144 | SUGGEST | Just curious, why add params key on pg 144? It might be beneficial to explain the difference. pg 121 pg 144 | 2017-04-07 | Thanks! | |
144 | SUGGEST | For the new_channel function, I think “subtopic” from pg 121 is more appropriate than “player” pgs 128 & 144 since we are going to use “moon” for both player1 and player2. pg 121 pg 128 & 144 | 2017-04-07 | Thanks, it does represent the player that created the game, so I'm inclined to keep it as is. | |
57 | ERROR | Using Elixir 1.4.2 sending the message :second after defining a handle_info/2 for :first doesn’t act like sending :first did before handling it — it causes an error: Compiling 1 file (.ex) (EXIT from #PID<0.120.0>) an exception was raised: Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help) (FunctionClauseError) no function clause matching in IslandsEngine.Game.handle_info/2 nil | 2017-04-07 | Thanks! | |
49 | TYPO | which would call the following service to see who should be which would call the following service to see who | 2017-04-06 | Thanks! | |
50 | TYPO | If we use mocks, keeping them in sync with the should be If we use mocks, keeping them in sync with the | 2017-04-05 | Thanks! | |
52 | ERROR | “Here’s the /lib/islands_engine.ex file that Elixir generated for us. In Elixir 1.4.1 the “use Application” directive is located in /lib/islands_engine/application.ex " use Application def start(_type, _args) do | 2017-04-07 | Thanks! | |
27 | ERROR | does not appear: | 2017-04-05 | Thanks! | |
14 | TYPO | Elixir State is Separate From Behavior -> Elixir State is Separated From Behavior | 2017-04-06 | Thanks, I think the original is correct. | |
73 | TYPO | The handle_call should be placed in “gen_server/lib/game.ex” but there is not a colored bar indicating where the code should go. def handle_call({:guess, player, coordinate}, _from, state) do | 2017-04-06 | Thanks, those colored bars are for the code that is bundled with the book, not the code in the project on your filesystem. I only put the final code for each chapter in the files bundled with the book, so you won't find the intermediate forms of the code there. | |
43 | TYPO | Just a typo. Repeat of of. “We’ve built up a tree structure of of agents…” | 2017-04-07 | Thanks! | |
21 | ERROR | I’m reading in iBooks. I tried copying the line | 2017-04-07 | Thanks! | |
23 | SUGGEST | In the definition of Coordinate.hit?/1, you don’t need to specify the Coordinate module - just def hit?(coordinate) do is fine. | 2017-04-07 | Thanks! | |
37 | ERROR | “media.pragprog.com/titles/lhelph/code/agent/lib/island_set.ex” IslandSet isn’t aliased here (it is in the book) | 2017-04-08 | Thanks! | |
29 | SUGGEST | Suggest removing the “== true” part of the Enum.all?/2 anonymous function, in the Island.forested?/1 definition, given Coordinate.hit?/1 returns true or false anyway. | 2017-04-08 | Thanks! | |
16 | SUGGEST | The “hit” in “whether the coordinate has been hit.” seems to be out of step with the “planting trees on islands” model. Maybe it should be “planted”? | 2017-04-09 | Thanks for this suggestion/question. Here's a little window into how my brain works, how I see this in my mind. I imagine a sort of catapult launching coconuts at coordinates. If one hits an island when it lands, a coconut tree will grow there. \n \nAs an aside, seeing how a coconut sprouts is kinda cool. I'm talking about full coconuts with the green sheath still attached, not the brown ones you might see in a grocery store. At any rate, they just sit on the surface of the sand on the beach. If the coconut senses that the conditions are right (moisture, sunlight, etc.), it sends out roots, and splits open to let a new tree shoot come out. The hull of the coconut will still be there for a time, and appears to act as protection for the young shoot. \n \nProbably TMI, but there you go. :) \n \nP.S. I think I'm going to leave it as "hit." :) | |
35 | SUGGEST | In the definition of Board.string_body/1, you can reuse the existing Board.get_coordinate/2 method instead of reimplementing it (with coord = Agent.get,….) | 2017-04-09 | Thanks! | |
44 | TYPO | Missing the word “is” between “this” and “keeping”. “The challenge with a structure like this keeping coordinate data in synch on both branches of the tree.” | 2017-04-10 | Thanks! | |
23 | TYPO | The case statement in the definition for in_island? contains an extra close paren. | 2017-04-12 | Thanks! | |
23 | SUGGEST | Half-way down the page, the code fragment at the wrapped end of the line is not formatted like the earlier code in the same statement. in_island: :my_island} end) | 2017-04-12 | Thanks! | |
65 | TYPO | At the bottom of page 65 it probably meant to have an image. It has this line instead | 2017-04-12 | Thanks! | |
77 | SUGGEST | Is the empty space from the middle of page 77 intended? | 2017-04-12 | Thanks, that was not intentional. I'll check into how that happened. | |
43 | ERROR | contains extra close paren in line: | 2017-04-12 | Thanks! | |
93 | ERROR | On page 93, when trying to run: iex> {:ok, pid} = Rules.start_link() I’m receiving the following… 14:06:03.181 [error] State machine #PID<0.115.0> terminating When server state = :undefined Reason for termination = :error::function_clause Callback mode = :undefined Stacktrace = [{IslandsEngine.Rules, :init, [:initialized], [file: ‘lib/rules.ex’, line: 12]}, I’ve copied the code directly from the book, the linked version, and the source download. I’m new to Elixir so I’m not exactly sure as to the why. Additionally, the full rules file is listed across pages 91 / 92 but I’m assuming we aren’t supposed to that at this point? | 2017-04-13 | Apologies! There's a typo in the code file for Rules.ex. If you change the start_link function to look like the following, you should be good to go: \n \ndef start_link do \n :gen_statem.start_link(__MODULE__, :ok, []) \nend | |
84 | TYPO | I think “We’re at the beginning of a sea change in web development.” was meant to be “We’re at the beginning of a sea of change in web development.” Love the book!! Great job!!! - Martin | 2017-04-14 | Thanks so much for you kind words about the book! Very glad you're enjoying it. <3 <3 <3 \n \nThanks for this report as well. I do think the original is correct, though. | |
115 | ERROR | Hi Lance, First of all, thanks for taking the time to write this book. It’s been a great read so far! I think that some code went missing in the example on page 115 of the pdf. def handle_call({:guess, player, coordinate}, _from, state) do Here opponent.board is directly accessed but it’s a pid. Shouldn’t it be: def handle_call({:guess, player, coordinate}, _from, state) do Rules.guess_coordinate(state.fsm, player) Regards, Bas | 2017-04-16 | Thanks so much for the kind words. So glad you're enjoying the book! \n \nThanks for this report as well. :) | |
116 | ERROR | Also on page 116 defp guess_reply({:error, :action_out_of_sequence}, _opponent_board, _coordinate) do matches on {:error, :action_out_of_sequence} but it should be just the atom :error. or the error is earlier in the book on page 105 where the catchall for def player1_turn({:call, from}, _, _state_data) do is inconsistent with the catchall for def player2_turn(_event, _caller_pid, state) do Regards, Bas | 2017-04-16 | Thanks again! | |
177 | ERROR | The wrong file name is given when adding IslandsInterface.Presence to the supervised children. Correct file name: The PDF incorrectly says the following: children = [ | 2017-04-16 | Thanks! | |
35 | TYPO | coord = get_coordinate(board, key)) exceeding closing parentheses | 2017-04-15 | Thanks! | |
148 | 144 | ERROR | Hello, I’m reading the part for calling IslandsEngine from the Phoenix controller : def test(conn, %{“name” => name}) do That part mismatch with the code juste below in the next paragraph : I think it will be fixed in the next release but I used the code for the chapter on supervisor from the code archive, and I needed to adapt this code to get a working solution. In the next examples in iex, we need the IslandsEngine.GameSupervisor code (in the code archive), setup correctly IslandEngine for calling the GameSupervice, and the code in phoenix should be : def test(conn, %{“name” => name}) do Cheers | 2017-04-16 | Thanks for reporting this. Yes, I got a little out of sync with the beta releases. This will be the correct code when the supervisor chapter is released. |
42 | 34 | SUGGEST | three methods are presented: def coordinate_hit?(board, key) do def set_coordinate_in_island(board, key, island) do def coordinate_island(board, key) do These are fine, but many elixir styleguides and linters emphasize starting pipechains with a raw value if possible. Perhaps instead: def coordinate_hit?(board, key) do def set_coordinate_in_island(board, key, island) do def coordinate_island(board, key) do this suggestion is applicable in other places where a pipe chain is used. | 2017-04-16 | Thanks! |
12 | TYPO | Multiple problems with agreement in the following sentence: “Processes all have mailboxes where they receives messages in a queue, then processes them in order.” | 2017-05-19 | Thanks! | |
12 | SUGGEST | You begin a paragraph with these words: “We can think about state on two levels. On the application level, . . . ” but you never (as far as I can see) tell us what the other level is. | 2017-05-19 | Thanks! | |
19 | SUGGEST | Beginning with the sentence “There are four functions we could use to do this” it seems clear that you’re talking about the documentation for Elixir’s OTP support library. However, you never actually move the reader to go look at those docs. An extra phrase or sentence could point to the docs, together with a footnote leading directly to the Agent start* functions. | 2017-05-19 | Thanks! | |
23 | TYPO | The third paragraph starts with “In in_island/1, we use”, but perhaps it should say “in_island?/1”. | 2017-05-19 | Thanks! | |
23 | SUGGEST | In the first paragraph, the purpose is different from the last code sample in the previous page, but the difference is rather subtle. Perhaps it would help to add a phrase like “as a Boolean” in the last sentence, like this: “a function that tells us as a Boolean value whether a coordinate” | 2017-05-19 | Thanks! | |
21 | SUGGEST | A footnote pointing to these functions could help to move the reader to actually read the docs. Context, this sentence: “The Agent module includes functions for both, Agent.get and Agent.update.” Links: | 2017-05-19 | Thanks! | |
23 | SUGGEST | The description in the third paragraph seems to confuse the layers of responsibility in the implementation. Here’s the wording: “we use the Coordinate.island/1 function to get the agent’s state, which is a coordinate struct. Then we use the value of the :in_island key in a case statement to determine the return value.” There are a couple of ways this could be clarified while keeping the implementation. Here’s one example: “we use the Coordinate.island/1 function to request the atom indicating which island a coordinate belongs to. Then we use the value of that atom in a case statement to determine the return value.” Another alternative works by looking into the island/1 function: “we ask the Coordinate.island/1 function to get the agent’s state, which is a coordinate struct, and return the value of the :in_island key. We use that value in a case statement to determine whether the coordinate is in an island or not.” | 2017-05-19 | Thanks! | |
26 | SUGGEST | The first paragraph begins “At the moment, the coordinate reports that it isn’t in an island at all.” This sentence seems unclear, because we have just seen that we have the ability to update a coordinate’s island, although you didn’t demonstrate the to_string/1 function with an island value other than :none. Maybe what you mean is closer to “At the moment we don’t have any islands to test our coordinate with.” Or maybe this paragraph has become extraneous. | 2017-05-19 | Thanks! | |
28 | SUGGEST | The first paragraph is “Let’s just check to make sure that works.” This might be an interesting place to talk about how to get the new code into iex. Do you recommend using recompile/0? or restarting iex? or . . . ? | 2017-05-19 | Thanks! | |
28 | SUGGEST | Last paragraph: “To make sure that the guard clause works, let’s try calling IslandsEngine. Maybe add (for clarity) “with a single coordinate not wrapped in a list.” | 2017-05-19 | Thanks! | |
30 | SUGGEST | This section closes with “Now that we can model one-to-many relationships, let’s move on to something just a little more complex.” This feels a little awkward because you have only demonstrated our island with a single coordinate. Perhaps the iex sample above could be slightly expanded to demonstrate two coordinates in one island. | 2017-05-19 | Thanks! | |
31 | TYPO | Disagreement in number: “and a coordinate belong to” | 2017-05-19 | Thanks! | |
32 | SUGGEST | Is it possible to use a Range in the definition of | 2017-05-19 | Thanks! | |
32 | SUGGEST | “function to enumerate over” . maybe should be “function to iterate over” | 2017-05-19 | Thanks! | |
34 | SUGGEST | I’m still sensing a little resistance to telling a coordinate which island it belongs to. I wonder if this was a premature optimization leading to duplication (and possibly corruption) of data. Since we anticipate a small number of islands, isn’t it enough to ask the collection of islands whether a given coordinate belongs to any of them? This thought is springboarding from the last phrase on the page, “setting a coordinate in an island” Maybe the reverse approach would be better, where an Island doesn’t care what its coordinates are, because Coordinates track that. One way or the other, it seems like there is duplicate knowledge: either an island should know its coordinates, or a coordinate should know which island it is in, but probably not both. | 2017-05-19 | Thanks! | |
37 | SUGGEST | Consider expanding the first sentence of the last paragraph to read “We use an empty IslandSet struct as the initial accumulator.” | 2017-05-19 | Thanks! | |
37 | SUGGEST | Consider clarifying the second sentence of the last paragraph as follows: “This will be passed into each iteration as the set[typeset the word ‘set’ as code] argument, the anonymous function will use Map.put/3 to update it with a new Island agent, and then Enum.reduce/3 will return it to act as an argument for the next iteration.” Also, I’m noticing several places where the word enumeration appears, but the word iteration seems to be intended. It is probably worth doing a search through the document to validate each use of this term. | 2017-05-19 | Thanks! | |
37 | SUGGEST | Consider adding “This won’t compile yet because we haven’t written the keys/0 function yet.” at the end of the paragraph just above the definition of initialized_set/0. | 2017-05-19 | Thanks! | |
38 | TYPO | Examples from iex in the middle of the page do not have color syntax highlighting, in contrast to the examples near the bottom of the page. | 2017-05-19 | Thanks! | |
38 | TYPO | Change “Our new keys/1 function fixes” to “Our new keys/0 function fixes” | 2017-05-19 | Thanks! | |
39 | SUGGEST | It would be nice if the output of the last iex command on the previous page could be kept together with the command, widows/orphans | 2017-05-19 | Thanks! | |
39 | SUGGEST | In the call-out “A New Construct”, would it be accurate and useful to add a final sentence as follows? “Similarly, &(&1.in_island) is equivalent to the fn state -> state.in_island end” | 2017-05-19 | Thanks! | |
40 | SUGGEST | By the start of the section on page 40 I’m feeling the lack of any code to enforce valid shapes on the coordinates added to an Island. You’re probably saving this for later in the book, but it might be nice to throw in a brief mention that we’re not checking now, but we’ll worry about it later. In a related note, it might be nice to add two coordinates (rather than a single coordinate) in the iex examples on page 40, just to make the concept of a List snap-in for the reader. | 2017-05-19 | Thanks! | |
41 | SUGGEST | There’s a disconnect between the last paragraph on p40 and the diagram at the top of p41. You’ve just listed three keys for the struct—:name, :board, and :island_set. But the diagram (which is showing hierarchy, not keys) doesn’t mention name. Would it make more sense if the diagram came between the 1st and 2nd paragraphs of this section? | 2017-05-19 | Thanks! | |
41 | SUGGEST | Throughout the chapter, whenever you create a to_string function and it displays the :none atom, I find myself wishing there could be an atom like :none that was more visibly/visually distinct from the atoms we want to be seeing. Maybe :n or something that looks really different from :atoll. | 2017-05-19 | Thanks! | |
42 | SUGGEST | The word “initialize” in this sentence, “Once we have that, we can initialize the struct and see what we get”, seems to be at odds with the following code example. The struct actually gets initialized before being bound to the variable, right? Maybe just replace “initialize” with “display”? | 2017-05-19 | Thanks! | |
46 | SUGGEST | For those of us who are trying to imagine reimplementing our business systems using the approaches and techniques in this book, the biggest question we have at this point is about persistence of long-lived things, like customers and payments and other things that we want to see months and years in the future. One or two sentences somewhere in this chapter could give us assurance that you’re planning to show us how to manage persistence later in the book. | 2017-05-19 | Thanks! | |
44 | SUGGEST | In the diagrams on page 44, would be accurate and/or useful to show only a single circle for Coord, with both Board and Island pointing to the same circle? | 2017-05-19 | Thanks! | |
45 | SUGGEST | In the code examples, the use of a1 might be unfortunate because in some typefaces, the 1 and the closing square brace look too similar. Instead of “Island.replace_coordinates(island, [a1])”, it looks like “Island.replace_coordinates(island, [all)”. For teaching purposes, a2 might be a more visible choice. | 2017-05-19 | Thanks! | |
117 | ERROR | The included source /gen_statem/lib/game.ex for this chapter does not include def call_demo(game) do def handle_call(:demo, _from, state) do | 2017-05-30 | Thanks! This is going to change a lot in future betas, but I'm going to leave it as is for now. | |
117 | ERROR | For the command: “iex> Game.guess_coordinate(game, :player1, :a1)” I was receiving the error: (EXIT from #PID<0.112.0>) an exception was raised: I modified the “defp guess_reply({:error, :action_out_of_sequence}, _opponent_board, _coordinate) do” signature in the game.ex file to “defp guess_reply(:error, _opponent_board, _coordinate) do” and all is working now. Love the book! Excellent coverage and methodology!! Best, | 2017-05-30 | Thanks! | |
118 | ERROR | Using Erlang/OTP 19, Elixir 1.4.2: Towards the bottom of the page, when player1 guesses out of turn, I get a “bad return from state function” error (command = “Game.guess_coordinate(game, :player1, :a1)”). My guess is that this is due to not having a function which matches the pattern returned by IslandsEngine.Rules. Here’s the console error dump: (EXIT from #PID<0.135.0>) {:bad_return_from_state_function, {:reply, {:error, :action_out_of_sequence}, :player2_turn, %IslandsEngine.Rules{player1: :islands_set, player2: :islands_set}}} Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help) Last event = {{:call, {#PID<0.379.0>, #Reference<0.0.2.1718>}}, When server state = {:player2_turn, Reason for termination = :error:{:bad_return_from_state_function, Callback mode = :state_functions Stacktrace = [{:gen_statem, :parse_event_result, 8, [file: ‘gen_statem.erl’, line: 1301]}, Best, | 2017-05-30 | Thanks! This is going to change a lot in future betas, but I'm going to leave it as is for now. | |
118 | ERROR | I figured out how to solve my state machine woes. I am not entirely sure of all the possible valid returns from :gen_statem, so I had to experiment quite a bit (Looks like I need to learn more Erlang). I did a couple of things to solve the issues I was experiencing. 1) I changed the return type for the default “catch all” event handler for player turns (i.e. player1_turn) to be “{:keep_state_and_data, {:reply, from, :error}}”. The routine wasn’t being called when a player guessed out of turn because the first clause wasn’t matching “{:call, from}”, but I was experiencing a timeout previously and I wasn’t sure how to match for the from term without the whole tuple, so I did… 2) I created a matching callback in the rules.ex file which matches on the invalid player guessing, which returns an error. Here’s the example code from player1_turn (player2_turn mirrors this example) (with the commented our attempts along the way): def player1_turn({:call, from}, {:guess_coordinate, :player2}, _state_data) do
It would be nice to have a more thorough explanation of the :gen_statem module for the final release of this book. Specifically, the types of function signatures which are allowed and the potential return values and their effects. Best, | 2017-05-30 | Thanks! This is going to change a lot in future betas, but I'm going to leave it as is for now. | |
35 | ERROR | in this bit of code; There is an unpaired right periphrasis. | 2017-05-19 | Thanks! | |
32 | ERROR | On page 31/32 you start the Board module. However, you omit the ‘end’ of the module. This is on contrast to the IslandSet module that you start on page 37, where you include the ‘end’ line. | 2017-05-19 | Thanks! | |
46 | SUGGEST | Throughout this chapter, the dimensions of the board require the reader to scroll back after displaying the current board. You also avoid displaying the entire board in your output examples included in the book. I would be OK with an extra paragraph in which you comment out parts of the | 2017-05-19 | Thanks! | |
23 | TYPO | def in_island?(coordinate) do I love your book | 2017-05-19 | Thanks! | |
21 | TYPO | “(…) that wraps the bare Agent.start_link/2 call.” should be Agent.start_link/1 (based on the code below the text): def start_link() do | 2017-05-19 | Thanks! | |
65 | ERROR | Right below the code for gen_server/lib/game.ex it reads: “Since we’re using the Coordinate module, let’s add that to the aliases we’re already using. But the actual aliases in game.ex are: alias IslandsEngine.{Game, Player} Could it be a misplaced paragraph? | 2017-05-19 | Thanks! | |
56 | SUGGEST | Formatting: the error message about “unexpected message” will naturally be wrapped by iex at the right margin. There’s no reason to avoid wrapping it in the book. | 2017-05-19 | Thanks! | |
57 | TYPO | It would be good to manage widows and orphans so that you don’t get a single line of code appearing on the page previous or subsequent to the other parts of the code sample. In this case, a lonely ‘end’ statement appears by itself at the top of page 57. | 2017-05-19 | That will happen at a later stage in the process. Thanks! | |
62 | TYPO | Formatting missing from last code sample on the page (IO.puts IslandsEngine…) | 2017-05-19 | Thanks! | |
65 | ERROR | On page 65 you talk about adding Coordinate to the aliases we’re already using. However, the next line does not match any alias statement already in game.ex. And if I add Coordinate to the existing statement, it only generates warnings, at least for the next couple of pages. If this alias is needed later or in a different file, the paragraph and statement should be relocated at the point in the book where it will make a difference. Also, the line is not formatted/syntax highlighted. | 2017-05-19 | Thanks! | |
69 | TYPO | Formatting missing from first line of second code sample, right after the words “Ok, let’s see what player1’s state looks like now.” | 2017-05-19 | Thanks! | |
123 | TYPO | join(game) should be join(game_channel) as the game object is not defined at this point. | 2017-05-30 | Thanks! | |
133 | TYPO | game.on(“player_added”, response => { should be game_channel.on(“player_added”, response => { the game object does not exist | 2017-05-30 | Thanks! | |
41 | ERROR | def start_link(name \\\\ :none) do Does not set the player’s name even if it is provided. As a result starting a game and supplying player1’s name does not set player1’s name. One would either have to explicitly set player1’s name OR adjust the start_link call to: Agent.start_link(fn -> %Player{board: board, island_set: island_set, name: name} end) Then if a name is not supplied the default parameter of “:none” is used but if a name is supplied it is also set in the Agent struct. | 2017-05-19 | Thanks! | |
21 | TYPO | On page 17 we define the ````’defmodule IslandsEngine.Coordinate’ in the file ‘lib/coordinate.ex’. On page 21 we add a ‘start_link’ function to that same defmodule and the green header above the code states its location as ‘agent/lib/coordinate.ex’. I believe this should simply be ‘lib/coordinate.ex’. | 2017-05-19 | Thanks! | |
127 | TYPO | In the second paragraph after the first definition of the Bicycle class, first sentence, you have the word “out” when you wanted “our” before the word “Bicycle”. It should say, “Rails tends to push us toward putting domain models, like our Bicycle class, …”. | 2017-05-30 | Thanks! | |
30 | SUGGEST | There may be missed “functional” opportunities with regard to the capture operator here. The ‘&(&1)’ usage is discussed in a sidebar on p.39(47) even though it could have been introduced as early as p.22(30) as a short form for ‘fn state -> state end’. def forested?(island) do defp coordinate_strings(island) do Then again, maybe calling out ‘&fn/#’ here first, may be a good lead up to ‘`&(&1)’ later. | 2017-05-19 | Thanks! | |
32 | SUGGEST | “This uses two “for” comprehensions." Wouldn’t it be more accurate(?) to state: “This comprehension uses two generators.” elixir-lang.org/getting-started/comprehensions.html#generators-and-filters | 2017-05-19 | Thanks! | |
38 | TYPO | The “nil” atoms should be “:none” in : iex> island_set = %IslandSet{} | 2017-05-19 | Thanks! | |
41 | TYPO | “Notice that we did not set a value for the :name key in the start_link/0 function.” 1. The arity is ‘start_link/1’ - default values for parameters do not affect arity. | 2017-05-19 | Thanks! | |
49 | TYPO | “given a list other users an individual follows” should probably be “given a list of other users an individual follows” | 2017-05-19 | ||
52 | TYPO | “We’ll see them in much more detail when we talk about Supervisors in the (as yet) unwritten chapter.design_for_recovery, and” Presumably this can now be replaced with a link to “Chapter 5. Process Supervision for recovery”. | 2017-05-19 | Thanks! | |
56 | SUGGEST | “iex> send(game, :first)” I understand why ‘send’ is used in this context. That being said I was under the impression that using raw concurrency primitives in connection with OTP code is generally a no-no. If that is the case, a quick “don’t do this in your code” reminder in a footnote may be useful. ‘handle_info’ is useful for all sorts of other reasons. | 2017-05-19 | Thanks! | |
58 | SUGGEST | “_from is the PID of the calling process,” hexdocs.pm/elixir/GenServer.html#t:from/0 2. I’m not sure why the documentation is so cavalier about exposing this type. “Designing for Scalability with Erlang/OTP (2016)”; Chapter 4. Generic Servers - Message Passing - Synchronizing Clients, p.89: Essentially the ‘from’ parameter is intended for use with OTP - if the server logic requires a “naked” PID then that PID should be part of the payload, the ‘request’ parameter (i.e. and not be extracted from the ‘from’ parameter). | 2017-05-30 | Thanks! | |
64 | TYPO | “iex> IO.puts IslandsEngine.Player.to_string(state.player2) the current version of ‘name_to_string’ actually prints “:none” instead of “none”, i.e. | 2017-05-19 | Thanks! | |
65 | SUGGEST | “Since we’re using the Coordinate module, let’s add that to the aliases we’re already using. We’ve just come off discussing ‘Game.set_island_coordinates’ (game.ex) - however the above text switches the context to the IslandSet (island_set.ex) module. This “switch” to ‘IslandSet’ needs to be made much clearer and explicit. | 2017-05-19 | Thanks! | |
70 | TYPO | “Let’s go back to Game.get_opponent/2.” In the current implementation it’s simply ‘Game.opponent/2’, not ‘Game.get_opponent/2’. | 2017-05-19 | Thanks! | |
70 | SUGGEST | The multi-clause nature of ‘opponent/2’ could potentially be more strikingly emphasized by using the short form function definition syntax, i.e. defp opponent(state, :player1), do: state.player2 | 2017-05-30 | Thanks! This will change significantly in future betas, so I'll leave it as is for now. | |
72 | SUGGEST | The pipeline in ‘handle_call({:guess, player, coordinate}, _from, state)’ could be made more “conformant” with the style guide e.g. {:reply, response, state} | 2017-05-30 | Thanks! This is going to change a lot in future betas, so I'm going to leave it as is for now. | |
75 | SUGGEST | “def win?(opponent) do” in (player.ex) Naming the argument supplied to ‘Player.win?/1’ ‘opponent’ in ‘Game.win_check’ makes sense; however the name of the parameter in the ‘Player.win?/1’ definition should simply be ‘player’ given that the function simply checks whether the specified player has won. Further exploring player.ex it is noticeable that agents seem to get parameter names like ‘player’, ‘island’, ‘island_set’, while the (game.ex) Game GenServer simply gets ‘pid’ (instead of simply ‘game’). Still in (player.ex) there are functions that don’t involve ‘player’ at all. It seems that ‘convert_coordinates(board, coordinates)’ might be more at home in the ‘Board’ module (board.ex). | 2017-05-30 | Thanks! This is going to change a lot in future betas, so I'm going to leave it as is for now. | |
76 | SUGGEST | “iex> GenServer.start_link(Game, {:ok, ”Frank“}, name: :islands_game)” It could beneficial to briefly mention in a footnote that there is some “keyword list syntactic sugar” magic at work here, i.e. that plain old ‘name: :islands_game’ is actually equivalent to ‘[{:name, :islands_game}]’ | 2017-05-30 | Thanks! | |
79 | SUGGEST | Why use/prefer def stop(pid) do def handle_cast(:stop, state) do ### over def stop(pid) do def terminate(_reason, _state) do ??? In any case mentioning the alternatives may be a good idea. | 2017-05-30 | Thanks! | |
94 | TYPO | extra parenthesis “agent/lib/board.ex Excerpt From: Lance Halvorsen. “Functional Web Development with Elixir, OTP, and Phoenix (for Bill Tihen).” iBooks. should be: “agent/lib/board.ex Excerpt From: Lance Halvorsen. “Functional Web Development with Elixir, OTP, and Phoenix (for Bill Tihen).” iBooks. | 2017-05-30 | Thanks! | |
133 | TYPO | Presumably it should be defmodule IslandsEngine.Application do rather than defmodule IslandsEngine do i.e. the code belongs in islands_engine/lib/islands_engine/application.ex rather than islands_engine/lib/islands_engine.ex | 2017-05-30 | Thanks! | |
135 | SUGGEST | Both iex> Application.start(:islands_engine) and iex> :application.start(:islands_engine)` are used and both work - but it does leave me wondering whether this was actually intentional (especially as Application doesn’t seem to expose everything that :application does, namely which_applications/0 ). | 2017-05-30 | Thanks! | |
137 | SUGGEST | At the point of $ mix phoenix.new islands_interface —no-ecto It wasn’t clear to me where in relation to the islands_engine directory we were supposed to be (i.e. islands_interface should be created as a sibling directory to islands_engine - so we are executing the command from islands_engine’s parent directory). | 2017-05-30 | Thanks! | |
157 | TYPO | iex> JOIN game:moon to IslandsInterface.GameChannel the last line is actually [info] Replied game:moon :ok | 2017-05-30 | Thanks! | |
161 | TYPO | Returned Greeting: World!} the closing brace is extraneous, should simply be: Returned Greeting: World! | 2017-05-30 | Thanks! | |
167 | TYPO | {:reply, {:error, %{reason: inspect(reasonn}}, socket} typo “reasonn}}” should be {:reply, {:error, %{reason: inspect(reason)}}, socket} | 2017-05-30 | Thanks! | |
171 | TYPO | iex> fsm = Game.call_demo(game).fsm I had to use this instead iex> fsm = IslandsEngine.Game.call_demo({:global,“game:moon”}).fsm - there is no previous “alias InterfaceEngine.Game” so iex> fsm = game.fsm would have sufficed but the “longer” version doesn’t rely on “game” being set. | 2017-05-30 | Thanks! This is going to change a lot in future betas, but I'm going to leave it as is for now. | |
180 | SUGGEST | The “if” in “authorized?” is redundant defp authorized?(socket, screen_name) do should be sufficient. | 2017-05-30 | :) Thanks! And actually, I think "if" is quite acceptable in Elixir. | |
169 | ERROR | For the JavaScript function set_island_coordinates, the params variable needs to be declared (“var”). Here’s the corrected function in its entirety: function set_island_coordinates(channel, player, island, coordinates) { Love the book!! Great job!! | 2017-05-30 | Thanks so much! | |
35 | TYPO | Extra closing parenthesis in: coord = get_coordinate(board, key)) | 2017-05-19 | Thanks! | |
91 | SUGGEST | After “The first one we need to tackle is callback_mode/0”, the code shown for “gen_statem/lib/rules.ex” shows a whole lot of new functions that have not yet been explained. | 2017-05-20 | Thanks! | |
117 | ERROR | At this point there was some bug I could not easily trace so I downloaded the source code following the links from the beginning of the book. But it doesn’t work either, now with: iex(2)> {:ok, game} = Game.start_link(“Betty”) (EXIT from #PID<0.120.0>) :function_clause 14:33:31.620 [error] State machine #PID<0.340.0> terminating When server state = :undefined Reason for termination = :error::function_clause Callback mode = :undefined Stacktrace = [{IslandsEngine.Rules, :init, [:initialized], | 2017-05-30 | ||
82 | ERROR | The handle_call/3 previously included opponent = opponent(state, player) But is later switched to Running the win checks in iex produces the following error (ArgumentError) argument error Which I fixed by restoring the opponent_board call | 2017-05-30 | Thanks! This is going to change a lot in future betas, but I'm going to leave it as is for now. | |
26 | 19 | ERROR | There is a missing ] on the end of the @enforce_keys line. It is in the code download but it is missing in the book. | 2017-06-04 | Thanks! |
31 | 24 | SUGGEST | In the code for the add_coordinate function, I would rewrite the case expression as follows: case Coordinate.new(row + row_offset, col + col_offset) do Doing it this way, anything that doesn’t match {:ok, coordinate} is considered an error and gets returned in the :halt tuple. By explicitly matching {:error, :invalid_coordinate}, if any other type of error was returned, it would not match one of the case patterns and cause a runtime error which would be more confusing to diagnose, potentially. Also, matching on error means that you don’t have to type {:error, :invalid_coordinate} twice and I am very lazy. | 2017-07-03 | Thanks! |
13 | SUGGEST | In Game introduction (page 5) The But the graphic in page 12 did no reflect the nomenclature. | 2017-06-04 | Thanks! | |
31 | 24 | SUGGEST | In the last paragraph, you should mention that you are redefining new/0 to new/2 and that you are not keeping the new/0 function around anymore. | 2017-06-04 | Thanks! |
34 | 27 | SUGGEST | Prior to introducing the code for the add/3 function, you might want to mention that the alias directive at the top of the source file should be changed from: alias MODULE to alias IslandsEngine.{Coordinate, Guesses} | 2017-06-04 | Thanks! |
40 | 33 | ERROR | You reference | 2017-07-03 | Thanks! |
40 | 33 | SUGGEST | Not sure how complete you want the code in the book to be. When you introduce the position_island/3 function, it relies on an alias for IslandsEngine.Island which has not been mentioned in the preceding discussion. | 2017-06-04 | Thanks! |
46 | 39 | TYPO | In the second paragraph after the first iex session, first sentence, the word “and” before the word “island” should be “an”. It should say, “Now let’s try for a guess that doesn’t forest an island or win.”. | 2017-06-04 | Thanks! |
39 | TYPO | “Perfect, that’ exactly what we expected.” -> “Perfect, that’s exactly what we expected.” | 2017-06-04 | Thanks! | |
21 | 14 | TYPO | In the last paragraph. “We can define a struct with row and col keys. Since we’ve aliased the Coordinate module, we can now refer to coordinate structs as %Coordinate{} instead of %IslandsEngine.Coordinate.” … instead of %IslandsEngine.Coordinate{} . ( missing curly braces ) | 2017-06-04 | Thanks! |
19 | TYPO | Missing closing square bracket on “@enforce_keys [:coordinates, :hit_coordinates” | 2017-06-18 | Thanks! | |
39 | ERROR | The code used to test if the guess is a miss is technically correct, but for the wrong reason. Coordinate.new/2 returns a tuple instead of just the coordinate. You have the correct code for this in the example below testing when the guess returns that a hit occurred. | 2017-07-03 | Thanks! | |
60 | TYPO | missing a do at the end of def def check(%Rules{state: :player2_turn} = rules, {:win_check, win_or_not}) case win_or_not do | 2017-07-21 | Thanks! | |
59, | TYPO | Pages 59, 60, 63 When running the code to test the :win and :no_win: clauses I think you are calling it incorrectly. Believe it should be like: {:ok, rules} = Rules.check(rules, {:win_check, :no_win}) and {:ok, rules} = Rules.check(rules, {:win_check, :win}) | 2017-07-21 | Thanks! | |
60 | ERROR | “we’ll need the same three clauses” -> “we’ll need the same two clauses” | 2017-07-21 | Thanks! | |
62 | TYPO | There is a typo in the word ‘should’ in the sentence: When one player sets their islands, they should no longer be able to position them, but the other player still shuld be able to. | 2017-07-21 | Thanks! | |
79 | TYPO | 2nd sentence under “Initializing GenServer State” heading: “.. the idea that we’re starting a new game process get’s buried in the arguments.” Change “get’s” to “gets”, or consider changing wording to something like, “… the idea that we’re starting a new game process becomes buried in the arguments” | 2017-07-29 | Thanks! | |
79 | SUGGEST | In the phrase, “To do this we’ll follow the GenServer pattern”, add a comma, e.g.: “To do this, we’ll follow the GenServer pattern” | 2017-07-29 | Thanks! | |
117 | TYPO | “it created a new Application for us with it’s own identity, above Phoenix itself” it’s -> its | 2017-08-02 | Thanks! | |
83 | ERROR | :no_win won’t match with any funciton defined, needs to be the tuple { :win_check, :no_win } iex> {:ok, rules} = Rules.check(rules, :no_win) | 2017-08-02 | Thanks! | |
88 | ERROR | In order to get {:error, :invalid_coordinate} on page 88 from the code def new(type, %Coordinate{} = upper_left) do The error case should return tuple {:error, error} | 2017-08-02 | Thanks, but the error case will already be a tuple. | |
107 | ERROR | Position Islands on GenServer def position_island(game, player, key, row, col) when player in @players, do : GenServer.call(game, { :position_island , player, key, row, col}) @players is not defined. | 2017-10-03 | Thanks! | |
89 | SUGGEST | I think the method should be defined before happy path examples on page 87. It seems it should be defined right after you display whole function on page 86: def handle_call({:position_island, player, key, row, col}, _from, state_data) | 2017-10-03 | Thanks! | |
109 | ERROR | on the game.ex on page 109 we are using: board = player_board(state_data, player), then on page 110 we are using iex to test, but player_board isn’t defined until page 112. | 2017-10-03 | Thanks! | |
112 | ERROR | @players isn’t defined yet. This is related with reported error #81916 def set_islands(game, player) when player in @players, do : GenServer.call(game, { :set_islands , player}) | 2017-10-03 | Thanks! | |
124 | ERROR | On the text refers: Notice that we’re pattern matching for a successful start of the game server but in the sample code: In order to make it work, we will need to add the supervisor on the IslandEngine, and start as a child below the Register. On the latest errors that I reported I used the page of the readers app ( #81916 #81918 #81923), not the pdf (sorry for the confusion) Ruben. | 2017-10-03 | Thanks! | |
63 | TYPO | “iex> {:ok, rules} = Rules.check(rules, :no_win)” should be "iex> {:ok, rules} = Rules.check(rules, {:win_check, :no_win}) | 2017-10-02 | Thanks! | |
84 | TYPO | iex> Game.demo_call(game) instead of Game.call_demo(game) at the third line from the bottom of the page. | 2017-10-02 | Thanks! | |
26 | SUGGEST | Additional rationale for using a Map to represent the Board: the guarantee of key uniqueness prevents 2 islands of the same type from being added to the Board. | 2017-10-04 | Thanks! | |
13 | SUGGEST | Maybe a colon would be better to introduce a list in a sentence containing 5 commas. “Just by describing that picture, we’ve identified four main entities, boards, islands, guesses, and coordinates.” could be “Just by describing that picture, we’ve identified four main entities: boards, islands, guesses, and coordinates.” | 2017-10-02 | Thanks! | |
18 | TYPO | Apostrophe “confusion” in a couple of places at the beginning of the page: “Now lets’ add coordinate2 to the hits set as well.” → “Now let’s add coordinate2 to the hits set as well.” “Now lets try adding coordinate1 to the hits set again.” → “Now let’s try adding coordinate1 to the hits set again.” | 2017-10-02 | Thanks! | |
29 | TYPO | There are a couple of places on this page where the “?” is missing from a function name: “There’s a great function to test for this, MapSet.disjoint/2.” → “There’s a great function to test for this, MapSet.disjoint?/2.” “We’ll use this overlaps/2 function later from a board to compare one island to all the existing ones.” → “We’ll use this overlaps?/2 function later from a board to compare one island to all the existing ones.” | 2017-10-02 | Thanks! | |
32 | ERROR | iex> :miss = Island.guess(dot, Coordinate.new(2, 2)) This is a false positive. It’s not giving a :miss because the coordinate is wrong. It’s giving a :miss because it’s trying to match {:ok, coordinate}. Using the actual coordinate for the dot island in the example would also result in a :miss. :miss = Island.guess(dot, Coordinate.new(2,1)) | 2017-10-03 | Thanks! | |
57 | SUGGEST | iex> Rules.check(rules, {:set_islands, :player2}) This call is also done on page 56 just a few lines above. It reads kind-of weird to have it in there twice. | 2017-10-03 | Thanks! | |
32 | ERROR | :miss = Island.guess(dot, Coordinate.new(2, 2)) even Island.guess(dot, Coordinate.new(4, 4)) will still return “false” even though this is dot island coordinate. Coordinate.new(2, 2) return a tuple instead of a coordinate. It will be nice to have | 2017-10-03 | Thanks! | |
70 | 63 | TYPO | This is what’s on the first line of the page in question: This is what it should be: | 2017-10-02 | Thanks! |
84 | TYPO | In the “handle_cast” function, change this return value: | 2017-10-03 | Thanks! | |
10 | TYPO | 5th paragraph, first sentence reads: But building an application with a framework from the very beginning makes it’s nearly impossible to maintain that separation. perhaps it should read: But building an application with a framework from the very beginning makes it nearly impossible to maintain that separation. | 2017-10-02 | Thanks! | |
10 | TYPO | 6th paragraph, last sentence reads: If frees us to focus on the pure domain of our application. it should read: It frees us to focus on the pure domain of our application. | 2017-10-02 | Thanks! | |
77 | SUGGEST | Lack of consistency in last page snippet: first line uses simply Game and the second one IslandsEngine.Game | 2017-11-09 | Thanks! | |
94 | 87 | SUGGEST | in iex snippet “alias IslandsEngine.Game” should be enough (other modules are not used). Similar fix can be applied into iex snipped on p. 90 (97 pdf). | 2017-11-09 | Thanks! |
u | TYPO | In the initial content of lib/islands_engine/island.ex, missing a closing square brace at the end of the line that begins, “@enforce_keys” | 2017-11-09 | Thanks! | |
kindl | ERROR | In the session exploring strategy for positioning islands, I got: iex(6)> offsets = [{0,0},{0,1},{1,0},{1,1}] The output shown in the book lacks the “ok:” before each item. | 2017-11-09 | Thanks! | |
126 | 120 | ERROR | Currently “ | 2017-11-09 | Thanks! |
127 | 121 | ERROR | Presented snippet does not terminate game after 15s. Instead “16:38:16.874 [error] IslandsEngine.Game #PID<0.117.0> received unexpected message in handle_info/2: :timeout” error is shown and the Game process is still alive (elixir v1.5.0, Erlang/OTP 19). | 2017-11-15 | Thanks! |
127 | 121 | ERROR | Currently: “ | 2017-11-09 | Thanks! |
140 | 135 | TYPO | You are referring to “part one” where game engine was build but it was done in part I and II. At the beginning of last page paragraph there is “Our task in part two” - but we are in part III. | 2017-11-09 | Thanks! |
191 | 186 | ERROR | Currently: “Open up /lib/islands_engine/application.ex”, should be “Open up /lib/islands_interface/application.ex” | 2017-11-09 | Thanks! |
155 | 160 | SUGGEST | executing Supervisor.which_children with :game_supervisor produced the following for me: (exit) exited in: GenServer.call(:game_supervisor, :which_children, :infinity) However, the following worked for me: | 2017-11-15 | Thanks! |
154 | 159 | SUGGEST | in the test function you use the following to start the game server: | 2017-11-15 | Thanks! |
60 | ERROR | Section “Player Two’s Turn” | 2017-11-16 | Thanks! | |
158 | 147 | SUGGEST | It is probably better to be consistent which mix task to use to generate phoenix project. You mentioned both phoenix.new and phx.new mix tasks. Regards, | 2018-01-02 | Thanks! |
79 | TYPO | `GenServer.cast(pid, {:demo, new_value})` should be `GenServer.cast(pid, {:demo_cast, new_value})` | 2018-01-02 | Thanks! | |
111 | TYPO | In the section “One For All”, the text below immediately refers to “all-for-one”. I think this is a mistake and should also be one-for-all. | 2018-01-02 | Thanks! | |
27 | SUGGEST | $ cd islands_engine | 2018-01-02 | Thanks! | |
79 | TYPO | Third paragraph from the bottom. “GenServer.start_link/3 three triggers” should be “GenServer.start_link/3 triggers”. | 2018-01-02 | Thanks! | |
92 | TYPO | Half way down the page there’s a list describing what the with needs to check. In the third item of that list “whether it forested and island” should be “whether it forested an island” | 2018-01-02 | Thanks! | |
110 | TYPO | Sixth paragraph down, “when starting child process” should either be “when starting a child process” or “when starting child processes”. | 2018-01-02 | Thanks! | |
117 | ERROR | In the code for the start/2 function you have the line import Supervisor.Spec, warn: false. This is not in my version of this function. For reference, I’m on Elixir 1.5.2. | 2018-01-02 | Thanks! | |
140 | ERROR | In the 5th paragraph it says that the callback module is in “/lib/islands_engine.ex”. However it is in “/lib/islands_engine/application.ex”. | 2018-01-02 | Thanks! | |
194 | ERROR | In the Phoenix installation notes it says to use “mix archive.install” with a URL ending in “phoenix_new.ez” in order to have access to the mix phx.new command. However the URL end in “phx_new.ez” instead. | 2018-01-02 | Thanks! | |
12 | TYPO | Context: second-to-last full paragraph on page, last sentence. Problem: “i” should be capitalized. “He also spoke to the community on behalf of the book at times when i could | 2018-01-02 | Thanks! | |
150 | ERROR | This is related to bug #82296, but if you install phoenix using the instructions in the book then IslandsEngine will not start properly without adding it to the applications list, because that wasn’t fixed until phoenix 1.3, which is not the version installed by the instructions in the book. | 2018-01-02 | Thanks! | |
50 | 35 | TYPO | at the top of the page | 2018-01-02 | Thanks! |
xvii | SUGGEST | On pages xvii and xviii, use of commas after chapter titles is inconsistent. For example: “In Chapter 2, Model Data and Behavior, on page 9, we’ll …” | |||
1 | TYPO | There are only two clauses in “We’re about to go exploring, and it’s going to be a blast.”, | |||
5 | SUGGEST | In “The players can move the islands around as much as they like until they say that they are set.”, the multiple uses of “they” are distracting and arguably ambiguous. I’d change this to something such as “The players can move their islands around as much as they like until they are satisfied.” | |||
10 | SUGGEST | I’d change “It frees us to focus …” to “This frees us to focus …” | |||
11 | SUGGEST | The reuse of the word “end” seems awkward here: “In the end, we end up …”. | |||
12 | SUGGEST | I’d explain the use of the term “engine”. | |||
13 | SUGGEST | The board images have poor contrast between blue and green (at least on my printer’s output). Be sure that the published book does not have this problem. Also, in the electronic version, I’d | |||
13 | TYPO | In “… three groups of coordinates, guessed coordinates …”, the comma should be a colon: | 2018-01-22 | ||
14 | SUGGEST | I’d change “We should be careful …” to “However, we should be careful …”. | |||
15 | SUGGEST | I’d rewrite “Make sure that you define | |||
16 | SUGGEST | Page 14 says “The first thing we’ll need is a Coordinate module that aliases itself.” Also, the code for the Coordinates and Guesses modules each include the line “alias MODULE”. However, the following iex sessions explicitly alias each of these modules. Why? | |||
18 | SUGGEST | I’d change “Once that rebinding happens, the original value will fall out of scope and be garbage collected.” to “Once that rebinding happens, the original value will fall out of scope and be garbage collected (unless another part of the process is still using it).”. | |||
18 | SUGGEST | I’d change “Now let’s add coordinate2 to the :hits set as well:” to “Now let’s add coordinate2 to the :hits set, as well:”. | |||
18 | SUGGEST | I’d change “It kept the set unique.” to “MapSet kept the set unique.”. | |||
19 | SUGGEST | I’d change “Let’s create new Island module at …” to “Let’s create a new Island module at …”. | |||
101 | ERROR | GenServer callback for :position_island does not handle {:error, :overlapping_island} in the with clause. As the reply is simply an echo of the error, the error handle could be simplified as follows: with ….. | |||
94 | SUGGEST | Rather than creating a new instance of Rules in IslandsEngine.Game.init/1 with %Rules{}: | |||
15 | ERROR | test | 2019-02-01 | ||
15 | TYPO | test | 2019-02-01 | ||
92 | ERROR | when calling :sys.replace_state, the parameter for the anonymous function (data) does not correspond to anything in the function body suggest changing the data parameter to state_data to read as: iex> state_data = :sys.replace_state(game, fn state_data -> | |||
174 | ERROR | with Elixir 1.6, the call to String.to_existing_atom(island) where island = “atoll” fails as: [debug] INCOMING “position_island” on “game:moon” to IslandsInterfaceWeb.GameChannel (ArgumentError) argument error in GameChannel, as a hack workaround, I alias IslandsEngine.Island and call Island.types() in the :ok branch of handle_in(“new_game”) note: the earlier call to String.to_existing_atom(player) succeeds | |||
14 | ERROR | Thank you for the book. I noticed that you are using the final placement of coordinate.ex here as a reference that includes model_data in the file path. Since that directory hasn’t been mentioned, it was a little confusing at first. It might be worth a mention or to just use the lib root that was just created. | |||
160 | ERROR | Template file path is wrong. /islands_interface/web/templates/page/index.html.eex maybe this should /islands_interface/lib/islands_interface_web/templates/page/index.html.eex | |||
171 | ERROR | Typo : JS command does not work Ought to be : | |||
19 | ERROR | The first code snippet does not compile. It’s missing an “alias MODULE”. Either that expression should be added or “%IslandsEngine.Island{…}” should be used in the “new” function. | |||
146 | ERROR | Adding a New Dependency…. “Only two steps are involved” The first is to add :islands_engine to the compile-time deps function. That is shown correctly. The second step, not described, is to add :islands_engine to the applications: list as follows: def application do | |||
11 | SUGGEST | The output when running the command to create the new project is missing the file .formatter.exs that is created in latest versions of elixir. | |||
16 | SUGGEST | The listing for the guesses.ex (when clicking on the link) has the following for alias: | |||
114 | ERROR | This is a really good book. The topics you covered are exactly what I was seeking. Very well done separation of concerns - a good example of how to structure things right. An errata(?): After following the precluding instructions in the book neither is behaving as specified in the book, which is to result in the process being stopped with a return of :ok. The supervised child process is shown as still active using: Supervisor.count_children(GameSupervisor) and/or Supervisor.which_children(GameSupervisor). Book results: Running mix compile.app (inside IslandsEngine.Mixfile) Online Source results: Running mix compile.app (inside IslandsEngine.Mixfile) (ArgumentError) argument error | |||
160 | ERROR | Really learning a lot from this book - I highly recommend it. I don’t know that this is an actual error or that my browser is not setup quite right. I would appreciate it if somebody could be kind enough to help me with this issue I am having. This is my environment: Running mix loadconfig (inside IslandsInterface.Mixfile) Running mix phoenix.new —version (inside IslandsInterface.Mixfile) | |||
87 | ERROR | Top of page, in the with statement, missing line (board is not defined) board <- player.board, full statement should be with {:ok, rules} <- Rules.check(state.rules, {:set_islands, player}), | |||
87 | ERROR | There is bug in my last bug report… Please do not consider my last bug report. | |||
72 | SUGGEST | After sending the message send(game, :first) iex reports this as an [error] not [warn] and also the word message is missing from ‘received unexpected in handle_info/2: :first’ text. | |||
47 | TYPO | “fine state machine” should probably be “finite state machine” | |||
80 | SUGGEST | On the book the with keyword is in a different line than in the actual game.ex file. Similar to do and else clauses with the {:ok, rules} .. in a different line. | |||
87 | SUGGEST | Last sentence of the page reads: Does the Rule module need to be aliased, as the following pages and examples work without aliasing it? | |||
103 | SUGGEST | (EXIT from #PID<0.122.0>) evaluator process exited with reason: :kaboom should be: (EXIT from #PID<0.122.0>) shell process exited with reason: :kaboom using elixir 1.6.4. | |||
108 | SUGGEST | The output in iex after running Game.child_spec(“Kusama”) %{id: IslandsEngine.Game, start: {IslandsEngine.Game, :start_link, [“Kusama”]}} | |||
139 | SUGGEST | The line: IslandsEngine.Worker.start_link(arg) is missing from the book, but it is generated in the code (Elixir 1.6.4). | |||
160 | ERROR | scope “/”, IslandsInterface do should be scope “/”, IslandsInterfaceWeb do | |||
162 | ERROR | [debug] Processing by IslandsInterface.PageController.test/2 should be [debug] Processing by IslandsInterfaceWeb.PageController.test/2 | |||
169 | ERROR | located at lib/islands_interface_web/islands_interface_web.ex should be lib/islands_interface_web.ex | |||
169 | ERROR | socket “/socket”, IslandsInterface.UserSocket should be socket “/socket”, IslandsInterfaceWeb.UserSocket | |||
170 | ERROR | web/channels/user_socket.ex should be lib/islands_interface_web/channels/user_socket.ex | |||
159 | ERROR | defmodule IslandsInterface.UserSocket do should be defmodule IslandsInterfaceWeb.UserSocket do | |||
160 | ERROR | iex> c “lib/islands_interface_web/channels/game_channel.ex” should be iex> c “lib/islands_interface_web/game_channel.ex” | |||
149 | TYPO | path `islands_interface/web/` should be `islands_interface_web/` in the file paths for templates, router.ex & controllers | |||
146 | ERROR | [Location 5855 in Kindle version; under “Adding a New Dependency”] I added ‘{: islands_engine, path: “../islands_engine”}’ to ‘deps/0’. But when I run ‘iex -S mix phx.server’ in ‘/lib/islands_interface’, I get the error message: Could not compile :islands_engine, no “mix.exs”, “rebar.config” or “Makefile” (pass
This file does exist, but in the ‘/_build’ for :islands_engine at the root level. ‘/islands_engine’ within the :islands_interface build is empty. See repo: on GitHub, English3000/Elixir/tree/master/functional_design Commenting out this line, the islands_interface is able to startup. (I also tried doing the other errata for this page too, but that didn’t work either.) | |||
246 | SUGGEST | Figured it out. I created the islands_interface inside islands_engine’s lib. I thought it was an umbrella project. I’d suggest tweaking the language to make crystal clear to navigate OUTSIDE the ENTIRE islands_engine project. Current language: Let’s change out of the the (sic) islands_engine into its parent directory. That way, when we run the phx.new task, we’ll create the interface directory parallel to the engine directory. Lance Halvorsen. Functional Web Development with Elixir, OTP, and Phoenix (Kindle Locations 5708-5710). The Pragmatic Bookshelf, LLC. Suggested: Let’s navigate outside of our islands_engine project. “That way, when we run the phx.new task, we’ll create the interface directory parallel to the engine directory.” The mixup is that there are 2 islands_engine folders in the project (one inside lib, one as the root folder). And readers may be in the one inside the lib, hence my mixup. | |||
185 | ERROR | The call to String.to_existing_atom here fails. For some reason, the atoms for the island types are not initialized when the app starts. | |||
187 | ERROR | With Phoenix 1.4, It’s not possible to return the board in the reply. Jason.Encoder throws an error. The only way to get it to work was sending the board inspected, but it looks really ugly on the browser client. | |||
175 | SUGGEST | The in-browser javascript console commands in this section have caused some confusion. Rather than: Which doesn’t work in Chrome. Maybe just start out using: var socket = new Phoenix.Socket(“/socket”, {}) Which does and the reader can then follow on from there. | |||
176 | ERROR | Just a clarification on my previous suggestion Hopefully this is of use to someone in future. Suggested commands for the “Establish a Client Connection” section: var socket = new Phoenix.Socket(“/socket”, {params: {token: window.userToken}}) socket.connect() function new_channel(subtopic, screen_name) { var game_channel = new_channel(“moon”, “moon”) function join(channel) { join(game_channel) | |||
185 | ERROR | To get around the problem described above, with the islands’ atom names not being initialized, you can update the islands_engine\\lib\\islands_engine\\islands.ex file like so… Add the following module param: Then, update the types() function to the following: This worked for me… hope it helps someone else. | |||
185 | SUGGEST | Nevermind my previous workaround… Gary Fleshman’s suggestion is probably a better way to hack this. | |||
177 | SUGGEST | Phoenix 1.4 comes with a new JSON parser - “Jason”. You’ll get an error if you will try to return a board in response: {:reply, {:ok, %{board: board}}, socket} I found two ways to fix it. The first one is sending the inspected board as we did it with tuples before: “inspect(board)”. But it won’t return a JSON object, just a string. The second one is much better. It allows us to get a standard JSON object. You need to add a code bellow to solve the issue (I’ve put it in “lib/islands_interface_web/channels/protocol.ex”, but it’s up to you): #== alias IslandsEngine.{Coordinate, Island} Protocol.derive(Jason.Encoder, Coordinate) defimpl Jason.Encoder, for: [MapSet] do | |||
190 | ERROR | I am reading the book at learning.oreilly.com, so don’t know exact page number. The issue is at “Connect the Channel to the Game” of Chapter 7. When I input “set_islands(game_channel, ”player2“)” in the second browser(player2), iex returns quite long error saying. (Protocol.UndefinedError) protocol Jason.Encoder not implemented for %IslandsEngine.Island{coordinates: #MapSet<[%IslandsEngine.Coordinate{col: 1, row: 1}, %IslandsEngine.Coordinate{col: 1, row: 3}, %IslandsEngine.Coordinate{col: 2, row: 1}, %IslandsEngine.Coordinate{col: 2, row: 2}, %IslandsEngine.Coordinate{col: 2, row: 3}]>, hit_coordinates: #MapSet<[]>} of type IslandsEngine.Island (a struct), Jason.Encoder protocol must always be explicitly implemented. If you own the struct, you can derive the implementation specifying which fields should be encoded to JSON: @derive {Jason.Encoder, only: [….]} PS> there is another issue. “player = String.to_existing_atom(player)” in “handle_in(”set_islands“, player, socket)” function of “game_channel.ex” needs an existing atom. So, I hard coded “String.to_atom(”player1“)” before it. Anyway, solved. | |||
0 | TYPO | on oreilly.com so no page number Go ahead and add {Registry, keys: :unique, name: Registry.Game)} to that list: there is a stray closing parenthesis in that instruction. | |||
0 | ERROR | on oreilly books Chapter 7 application.ex where does the supervisor function come from? children = [ compiling this results in an exception. | |||
160 | SUGGEST | Browser does not support “require(”phoenix") since Require is Node-only. There is a workaround: put it on the window directly. Example: in your app.js put the following: — example start window.kconnect = () => socket.connect() window.kprint = (str) => console.log(str) window.new_channel = (subtopic, screen_name) => { window.join = (channel) => { window.leave = function(channel) { — example end Then you call kconnect, make a new channel and store it in a variable, and call join with the channel object as an argument. This isn’t obvious with the code in the book. Maybe webpack stopped supporting “require” calls or something. Maybe it never did support that. |