The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!
Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.
What do I do wrong? I paste in the code from my own editor,
it should be identical to the book listing:
defmodule TodoList do
def new, do: HashDict.new
def add_entry(todo_list, date, title) do
HashDict.update(
todo_list,
date,
[title],
fn(titles) -> [title | titles] end
)
end
def entries(todo_list, date) do
HashDict.get(todo_list, date, [])
end
end
Then, I run:
iex(1)> c "simple_todo.exs"
simple_todo.exs:1: warning: redefining module TodoList
[TodoList]
You get the correct result. But you don't take it into some variable, and thus your mylist still "points" to the original data.
Keep in mind that there's no in-place data mutation in Elixir. You can't change the content of mylist. You can only invoke the function which will return the new data based on the original one. You need to take the result of the function in another variable. You can also take the result into the variable of the same name in which case the variable is "rebound" - it will point to a different data, while the original data is unaffected.
This should prove that your data is immutable. Var list2 holds the modified version of the to-do list, but the original is not modified. As explained in section 2.4.5, as much of the data as possible will be shared between two instances.
def add_entry(todo_list, date, title) do
HashDict.update(
todo_list,
date,
title, [b]# <- simple 'title' instead of [title][/b]
fn(titles) -> [title | titles] end
)
end
If one permist entering a list of titles, the result of TodoList.entries(....) becomes ugly: not simply a list of entries, but probably also a list of simple entries mixed with lists of entries: e.g.
[["bookmaker","Vet"], "Dentist",.....]
Yes, this happens because your first add_entry will insert a "plain" title (i.e. not a list). The next add_entry then builds a so called improper list, where the tail is not a list. Instead the tail will be the first entry you added, and the result can then look weird. What's worse, it can be hard to interpret such result, because you may end up with a potentially deep nested list.
This is why we use [title] in the original code. It ensures that when the first entry is inserted for some date, we immediately create a one-element list. Subsequent invocations of add_entry will just prepend the new element to the properly constructed list, so we know we always have a list of titles.
It's also worth looking how HashDict.update works. It's explained in the help for Dict.update.
I'm not sure I understand (but I still haven't read about improper lists).
But when I run the program inserting simple 'title' (not [title]), it runs like this:
iex(1)> c "simple_todo.exs"
simple_todo.exs:1: warning: redefining module TodoList
[TodoList]
iex(2)> newlis = TodoList.new()
#HashDict<[]>
iex(3)> newlis2 = TodoList.add_entry(newlis,{2015,11,05},"Doctor")
#HashDict<[{{2015, 11, 5}, ["Doctor"]}]>
iex(4)> newlis3 = TodoList.add_entry(newlis2,{2015,11,05},"Dentist")
#HashDict<[{{2015, 11, 5}, ["Dentist", "Doctor"]}]>
iex(5)> newlis3 = TodoList.add_entry(newlis3,{2015,11,05},"Vet")
#HashDict<[{{2015, 11, 5}, ["Vet", "Dentist", "Doctor"]}]>
iex(6)> [head|tail] = TodoList.entries(newlis3,{2015,11,05})
["Vet", "Dentist", "Doctor"]
iex(7)> head
"Vet"
iex(8)> tail
["Dentist", "Doctor"] # <- Both a head and a teil. Looks like a normal list to me ??
iex(9)>
iex(9)> [h|t] = ["Dentist", "Doctor"]
["Dentist", "Doctor"]
iex(10)> t
["Doctor"]
iex(11)> [h|t] = ["Doctor"]
["Doctor"]
iex(12)> t
[] # <- Last element of the list
Something is strange there. The output is definitely not expected if you use just title, instead of [title]. My guess is that the newer version of the module is somehow not loaded. I'd suggest you exit the shell, make sure to delete any .beam files and try again. Maybe by just directly copy-pasting to the shell.
Here's how it looks on my end:
iex(1)> defmodule TodoList do
def new, do: HashDict.new
def add_entry(todo_list, date, title) do
HashDict.update(
todo_list,
date,
title,
fn(titles) -> [title | titles] end
)
end
def entries(todo_list, date) do
HashDict.get(todo_list, date, [])
end
end
iex(2)> newlis = TodoList.new
#HashDict<[]>
iex(3)> newlis2 = TodoList.add_entry(newlis,{2015,11,05},"Doctor")
#HashDict<[{{2015, 11, 5}, "Doctor"}]>
iex(4)> newlis3 = TodoList.add_entry(newlis2,{2015,11,05},"Dentist")
#HashDict<[{{2015, 11, 5}, ["Dentist" | "Doctor"]}]>
Notice the character | between two elements. This is an indication you have an improper list. The tail of the list is not the list itself. This is quite expected, since in the first update, we created a string under the given key. The next update, however, assumes the value is list, and appends with
[title | titles]
But titles is not a list itself. Now, that code won't actually break, but you'll get an improper list. This now may become a problem, because many functions won't work anymore:
In practice, improper lists are something you probably never need to use. I don't remember I've ever needed them.
The bottom line is to keep in mind you always construct the desired data format. In the given example, we want to keep the list of entries under the given date. Thus, by using [title] in the third argument to HashDict.update, we make sure that we'll construct the list when adding the first entry on a given date. Subsequent updates then prepend new entries with [title | titles], and that will work properly only if whatever is in titles is in fact a list.