From 1f1112c8883c2f39fd6add56ae6b0b16a98c8f99 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 3 May 2016 17:34:38 -0500 Subject: [PATCH 01/16] Add koan illustrating that a process is referenced by a pid --- lib/koans/10_processes.ex | 4 ++++ test/koans/processes_koans_test.exs | 1 + 2 files changed, 5 insertions(+) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 4ad235f..380a194 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -11,6 +11,10 @@ defmodule Processes do assert information[:status] == ___ end + koan "Processes are referenced by their process ID (pid)" do + assert is_pid(self) == ___ + end + koan "New processes are spawned functions" do pid = spawn(fn -> nil end) diff --git a/test/koans/processes_koans_test.exs b/test/koans/processes_koans_test.exs index d17ec9a..7ba6e14 100644 --- a/test/koans/processes_koans_test.exs +++ b/test/koans/processes_koans_test.exs @@ -7,6 +7,7 @@ defmodule ProcessesTests do true, :running, true, + true, "hola!", :how_are_you?, {:waited_too_long, "I am impatient"}, From 2629dc05237590c2807427440ae62c4254690471 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 3 May 2016 17:35:04 -0500 Subject: [PATCH 02/16] whitespace --- test/koans/processes_koans_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/koans/processes_koans_test.exs b/test/koans/processes_koans_test.exs index 7ba6e14..67d29c1 100644 --- a/test/koans/processes_koans_test.exs +++ b/test/koans/processes_koans_test.exs @@ -18,7 +18,7 @@ defmodule ProcessesTests do false, :normal, :normal, - ] + ] test_all(Processes, answers) end From 92ccbcd68d3b0b26d0431ab760ea40b733a515e0 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 3 May 2016 17:35:48 -0500 Subject: [PATCH 03/16] Tweak descriptions of a couple process koans --- lib/koans/10_processes.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 380a194..80b93b7 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -21,7 +21,7 @@ defmodule Processes do assert Process.alive?(pid) == ___ end - koan "You can send messages to processes" do + koan "Processes can send and receive messages; it's like a mailbox!" do send self, "hola!" receive do @@ -97,7 +97,7 @@ defmodule Processes do assert Process.alive?(pid) == ___ end - koan "Parent processes can be informed about exiting children, if they trap and link" do + koan "Parent processes can trap exits for children they are linked to" do Process.flag(:trap_exit, true) spawn_link(fn -> Process.exit(self, :normal) end) From cdfca2a24a67fec8566f40b6818955355a2fa245 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 3 May 2016 17:36:14 -0500 Subject: [PATCH 04/16] Extract variable for spawned function --- lib/koans/10_processes.ex | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 80b93b7..b3ba744 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -30,10 +30,13 @@ defmodule Processes do end koan "A common pattern is to include the sender in the message, so that it can reply" do - pid = spawn(fn -> receive do - {:hello, sender} -> send sender, :how_are_you? - end - end) + greeter = fn -> + receive do + {:hello, sender} -> send sender, :how_are_you? + end + end + + pid = spawn(greeter) send pid, {:hello, self} assert_receive ___ From 322799a655032a11d6dd57a1a91ff6a9113a6521 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 3 May 2016 17:41:55 -0500 Subject: [PATCH 05/16] Remove koans illustrating self-exit I don't really like these examples, because the process would exit anyway when the function returns... Also having to sleep is not ideal. --- lib/koans/10_processes.ex | 13 ------------- test/koans/processes_koans_test.exs | 2 -- 2 files changed, 15 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index b3ba744..3eb9f85 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -53,12 +53,6 @@ defmodule Processes do assert_receive ___ end - koan "Killing a process will terminate it" do - pid = spawn(fn -> Process.exit(self, :kill) end) - :timer.sleep(500) - assert Process.alive?(pid) == ___ - end - koan "You can also terminate processes other than yourself" do pid = spawn(fn -> receive do end end) @@ -93,13 +87,6 @@ defmodule Processes do assert Process.alive?(pid) == ___ end - koan "Exiting normally yourself on the other hand DOES terminate you" do - pid = spawn(fn -> Process.exit(self, :normal) end) - :timer.sleep(100) - - assert Process.alive?(pid) == ___ - end - koan "Parent processes can trap exits for children they are linked to" do Process.flag(:trap_exit, true) spawn_link(fn -> Process.exit(self, :normal) end) diff --git a/test/koans/processes_koans_test.exs b/test/koans/processes_koans_test.exs index 67d29c1..81f16e7 100644 --- a/test/koans/processes_koans_test.exs +++ b/test/koans/processes_koans_test.exs @@ -11,11 +11,9 @@ defmodule ProcessesTests do "hola!", :how_are_you?, {:waited_too_long, "I am impatient"}, - false, {:multiple, [true, false]}, {:exited, :random_reason}, true, - false, :normal, :normal, ] From 1f24de98f4c561fe0ae14a9d49a8cfe66b122afe Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 3 May 2016 17:47:58 -0500 Subject: [PATCH 06/16] Rearrange existing koans to paint a clearer picture --- lib/koans/10_processes.ex | 30 ++++++++++++++--------------- test/koans/processes_koans_test.exs | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 3eb9f85..2c5397b 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -21,6 +21,21 @@ defmodule Processes do assert Process.alive?(pid) == ___ end + koan "You can kill processes other than yourself" do + pid = spawn(fn -> receive do end end) + + assert Process.alive?(pid) == ___ + Process.exit(pid, :kill) + assert Process.alive?(pid) == ___ + end + + koan "However, trying to exit normally has no effect" do + pid = spawn(fn -> receive do end end) + Process.exit(pid, :normal) + + assert Process.alive?(pid) == ___ + end + koan "Processes can send and receive messages; it's like a mailbox!" do send self, "hola!" @@ -53,14 +68,6 @@ defmodule Processes do assert_receive ___ end - koan "You can also terminate processes other than yourself" do - pid = spawn(fn -> receive do end end) - - assert Process.alive?(pid) == ___ - Process.exit(pid, :kill) - assert Process.alive?(pid) == ___ - end - koan "Trapping will allow you to react to someone terminating the process" do parent = self pid = spawn(fn -> @@ -80,13 +87,6 @@ defmodule Processes do assert_receive ___ end - koan "Trying to quit normally has no effect" do - pid = spawn(fn -> receive do end end) - Process.exit(pid, :normal) - - assert Process.alive?(pid) == ___ - end - koan "Parent processes can trap exits for children they are linked to" do Process.flag(:trap_exit, true) spawn_link(fn -> Process.exit(self, :normal) end) diff --git a/test/koans/processes_koans_test.exs b/test/koans/processes_koans_test.exs index 81f16e7..cbbe177 100644 --- a/test/koans/processes_koans_test.exs +++ b/test/koans/processes_koans_test.exs @@ -8,12 +8,12 @@ defmodule ProcessesTests do :running, true, true, + {:multiple, [true, false]}, + true, "hola!", :how_are_you?, {:waited_too_long, "I am impatient"}, - {:multiple, [true, false]}, {:exited, :random_reason}, - true, :normal, :normal, ] From 6126d5a83634bb771489f430e6e5ab0e4fd685ec Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 3 May 2016 17:48:21 -0500 Subject: [PATCH 07/16] Add koan illustrating tail recursion --- lib/koans/10_processes.ex | 25 +++++++++++++++++++++++++ test/koans/processes_koans_test.exs | 1 + 2 files changed, 26 insertions(+) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 2c5397b..68e0eef 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -57,6 +57,31 @@ defmodule Processes do assert_receive ___ end + def yelling_echo_loop do + receive do + {caller, value} -> + send caller, String.upcase(value) + yelling_echo_loop + end + end + + koan "Use tail recursion (calling a function as the very last statement) to receive multiple messages" do + pid = spawn &yelling_echo_loop/0 + + send pid, {self, "o"} + send pid, {self, "hai"} + + receive do + msg -> assert msg == ___ + end + + receive do + msg -> assert msg == ___ + end + + Process.exit(pid, :kill) + end + koan "Waiting for a message can get boring" do parent = self spawn(fn -> receive do diff --git a/test/koans/processes_koans_test.exs b/test/koans/processes_koans_test.exs index cbbe177..f3d3500 100644 --- a/test/koans/processes_koans_test.exs +++ b/test/koans/processes_koans_test.exs @@ -12,6 +12,7 @@ defmodule ProcessesTests do true, "hola!", :how_are_you?, + {:multiple, ["O", "HAI"]}, {:waited_too_long, "I am impatient"}, {:exited, :random_reason}, :normal, From 65b9af48b46ecf5fb0f1259972b6181cde341b44 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 3 May 2016 19:05:35 -0500 Subject: [PATCH 08/16] Add example of process as state --- lib/koans/10_processes.ex | 28 ++++++++++++++++++++++++++++ test/koans/processes_koans_test.exs | 1 + 2 files changed, 29 insertions(+) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 68e0eef..aadb3a0 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -82,6 +82,34 @@ defmodule Processes do Process.exit(pid, :kill) end + def state(value) do + receive do + {caller, :get} -> + send caller, value + state(value) + {caller, :set, new_value} -> + state(new_value) + end + end + + koan "Processes can be used to hold state" do + initial_state = "foo" + pid = spawn fn -> + state(initial_state) + end + + send pid, {self, :get} + receive do + value -> assert value == ___ + end + + send pid, {self, :set, "bar"} + send pid, {self, :get} + receive do + value -> assert value == ___ + end + end + koan "Waiting for a message can get boring" do parent = self spawn(fn -> receive do diff --git a/test/koans/processes_koans_test.exs b/test/koans/processes_koans_test.exs index f3d3500..794335a 100644 --- a/test/koans/processes_koans_test.exs +++ b/test/koans/processes_koans_test.exs @@ -13,6 +13,7 @@ defmodule ProcessesTests do "hola!", :how_are_you?, {:multiple, ["O", "HAI"]}, + {:multiple, ["foo", "bar"]}, {:waited_too_long, "I am impatient"}, {:exited, :random_reason}, :normal, From f7cced591318d4df1584d48ad742ea0d81bc5281 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Wed, 4 May 2016 15:08:17 -0500 Subject: [PATCH 09/16] Replace multiple receives with assert_receives :heart: #101 --- lib/koans/10_processes.ex | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index aadb3a0..7b6ee8c 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -71,13 +71,8 @@ defmodule Processes do send pid, {self, "o"} send pid, {self, "hai"} - receive do - msg -> assert msg == ___ - end - - receive do - msg -> assert msg == ___ - end + assert_receive ___ + assert_receive ___ Process.exit(pid, :kill) end @@ -99,15 +94,11 @@ defmodule Processes do end send pid, {self, :get} - receive do - value -> assert value == ___ - end + assert_receive ___ send pid, {self, :set, "bar"} send pid, {self, :get} - receive do - value -> assert value == ___ - end + assert_receive ___ end koan "Waiting for a message can get boring" do From 4e4e35f575c84970cb6041b55976585c1a95fdbe Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Wed, 4 May 2016 15:13:35 -0500 Subject: [PATCH 10/16] Remove process about killing others :laughing: --- lib/koans/10_processes.ex | 8 -------- test/koans/processes_koans_test.exs | 1 - 2 files changed, 9 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 7b6ee8c..8d89b99 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -21,14 +21,6 @@ defmodule Processes do assert Process.alive?(pid) == ___ end - koan "You can kill processes other than yourself" do - pid = spawn(fn -> receive do end end) - - assert Process.alive?(pid) == ___ - Process.exit(pid, :kill) - assert Process.alive?(pid) == ___ - end - koan "However, trying to exit normally has no effect" do pid = spawn(fn -> receive do end end) Process.exit(pid, :normal) diff --git a/test/koans/processes_koans_test.exs b/test/koans/processes_koans_test.exs index 794335a..ff0ad38 100644 --- a/test/koans/processes_koans_test.exs +++ b/test/koans/processes_koans_test.exs @@ -8,7 +8,6 @@ defmodule ProcessesTests do :running, true, true, - {:multiple, [true, false]}, true, "hola!", :how_are_you?, From 75c54e0585d130dc5f5fbd954348674e65a97981 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Wed, 4 May 2016 15:13:49 -0500 Subject: [PATCH 11/16] Remove explanation of tail recursion Curious readers might look it up, so just mention it in passing and let them focus on the task at hand: handling multiple messages. --- lib/koans/10_processes.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 8d89b99..772151b 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -57,7 +57,7 @@ defmodule Processes do end end - koan "Use tail recursion (calling a function as the very last statement) to receive multiple messages" do + koan "Use tail recursion to receive multiple messages" do pid = spawn &yelling_echo_loop/0 send pid, {self, "o"} From 6aae32e672ba26c58e8b22be973d0452d0335ef0 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Wed, 4 May 2016 15:17:52 -0500 Subject: [PATCH 12/16] Explain FIFO nature of message in own example --- lib/koans/10_processes.ex | 10 +++++++++- test/koans/processes_koans_test.exs | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 772151b..45bafe4 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -28,7 +28,7 @@ defmodule Processes do assert Process.alive?(pid) == ___ end - koan "Processes can send and receive messages; it's like a mailbox!" do + koan "Processes can send and receive messages" do send self, "hola!" receive do @@ -36,6 +36,14 @@ defmodule Processes do end end + koan "Received messages are queued, first in first out" do + send self, "hola!" + send self, "como se llama?" + + assert_receive ___ + assert_receive ___ + end + koan "A common pattern is to include the sender in the message, so that it can reply" do greeter = fn -> receive do diff --git a/test/koans/processes_koans_test.exs b/test/koans/processes_koans_test.exs index ff0ad38..d3dd9a5 100644 --- a/test/koans/processes_koans_test.exs +++ b/test/koans/processes_koans_test.exs @@ -10,6 +10,7 @@ defmodule ProcessesTests do true, true, "hola!", + {:multiple, ["hola!", "como se llama?"]}, :how_are_you?, {:multiple, ["O", "HAI"]}, {:multiple, ["foo", "bar"]}, From 12681f3aa2127357d9c3121c79b0e6626faf3ac9 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Wed, 4 May 2016 15:18:16 -0500 Subject: [PATCH 13/16] Interleave sends and receives --- lib/koans/10_processes.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 45bafe4..7027b7e 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -69,9 +69,9 @@ defmodule Processes do pid = spawn &yelling_echo_loop/0 send pid, {self, "o"} - send pid, {self, "hai"} - assert_receive ___ + + send pid, {self, "hai"} assert_receive ___ Process.exit(pid, :kill) From e4771a46a409feadbfbc6a701b3cebc9d8e618a2 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Wed, 4 May 2016 15:29:20 -0500 Subject: [PATCH 14/16] Remove other process killing example The narrative no longer reads consistently... Just :hocho: it --- lib/koans/10_processes.ex | 7 ------- test/koans/processes_koans_test.exs | 1 - 2 files changed, 8 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 7027b7e..2772845 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -21,13 +21,6 @@ defmodule Processes do assert Process.alive?(pid) == ___ end - koan "However, trying to exit normally has no effect" do - pid = spawn(fn -> receive do end end) - Process.exit(pid, :normal) - - assert Process.alive?(pid) == ___ - end - koan "Processes can send and receive messages" do send self, "hola!" diff --git a/test/koans/processes_koans_test.exs b/test/koans/processes_koans_test.exs index d3dd9a5..d258c53 100644 --- a/test/koans/processes_koans_test.exs +++ b/test/koans/processes_koans_test.exs @@ -8,7 +8,6 @@ defmodule ProcessesTests do :running, true, true, - true, "hola!", {:multiple, ["hola!", "como se llama?"]}, :how_are_you?, From 4710ad1c63c0d37772225a5bfc61223dab68ec11 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Wed, 4 May 2016 15:29:57 -0500 Subject: [PATCH 15/16] Use parens around spawn calls --- lib/koans/10_processes.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 2772845..3f452f7 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -59,7 +59,7 @@ defmodule Processes do end koan "Use tail recursion to receive multiple messages" do - pid = spawn &yelling_echo_loop/0 + pid = spawn(&yelling_echo_loop/0) send pid, {self, "o"} assert_receive ___ @@ -82,9 +82,9 @@ defmodule Processes do koan "Processes can be used to hold state" do initial_state = "foo" - pid = spawn fn -> + pid = spawn(fn -> state(initial_state) - end + end) send pid, {self, :get} assert_receive ___ From 4bf62aa301753f29ff69de4dc71ef99d9ed8d83c Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Wed, 4 May 2016 15:32:37 -0500 Subject: [PATCH 16/16] Replace explicit kill with linked process Since the spawned process is linked and each koan is run inside its own process, it gets killed by association. --- lib/koans/10_processes.ex | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex index 3f452f7..10b4a49 100644 --- a/lib/koans/10_processes.ex +++ b/lib/koans/10_processes.ex @@ -59,15 +59,13 @@ defmodule Processes do end koan "Use tail recursion to receive multiple messages" do - pid = spawn(&yelling_echo_loop/0) + pid = spawn_link(&yelling_echo_loop/0) send pid, {self, "o"} assert_receive ___ send pid, {self, "hai"} assert_receive ___ - - Process.exit(pid, :kill) end def state(value) do