Skip to content

Instantly share code, notes, and snippets.

@alco
Created March 27, 2012 23:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alco/2221616 to your computer and use it in GitHub Desktop.
Save alco/2221616 to your computer and use it in GitHub Desktop.
Updated Elixir chat demo
# !!!
# This is an outdated version of the code. See https://gist.github.com/2783092.
defmodule Chat.Client do
# In all of the following functions 'server' stands for the server's pid
def join(server) do
send server, :join
end
def say(server, message) do
send server, { :say, message }
end
def leave(server) do
send server, :leave
end
# Receive a pending message from 'server' and print it
def flush(server) do
receive do
# The caret '^' is used to match against the value of 'server',
# it is a basic filtering based on the sender
match: { ^server, {:message, message} }
IO.puts message
end
end
# Send 'message' to 'server' and wait for a reply
# Notice how this function is declared using 'defp' meaning
# it's private and can only be called inside this module
defp send(server, message) do
server <- { Process.self, message }
receive do
match: { ^server, response }
response
after: 1000
IO.puts "Connection to room timed out"
:timeout
end
end
end
defmodule Chat.Server do
# A Room record acts as a reference to 'this' or 'self' in other langs
defrecord Room, clients: []
# The main receive loop
def msg_loop(room) do
receive do
match: {pid, :join }
notify_all room, Process.self, "Some user with pid #{inspect pid} joined"
pid <- { Process.self, :ok }
# The 'prepend_clients' function is generated automatically.
# See section 4.1.1 in http://elixir-lang.org/getting_started/4.html
msg_loop room.prepend_clients([pid])
match: {pid, {:say, message}}
notify_all room, pid, "#{inspect pid}: " <> message
pid <- { Process.self, :ok }
msg_loop room
match: {pid, :leave}
pid <- { Process.self, :ok }
# 'update_clients' is another automatically generated function
new_room = room.update_clients(fn(clients) -> :lists.delete(pid, clients) end)
notify_all new_room, Process.self, "User with pid #{inspect pid} left"
msg_loop new_room
end
end
# Send 'message' to all clients except 'sender'
defp notify_all({Room, clients}, sender, message) do
Enum.each clients, fn(pid) ->
if pid != sender do
pid <- { Process.self, {:message, message} }
end
end
end
end
# Init a Room record with the current process
room = Chat.Server.Room.new(clients: [Process.self])
# Spawn a server process
server_pid = spawn fn() -> Chat.Server.msg_loop room end
# Spawn another process as client
spawn fn() ->
Chat.Client.join server_pid
Chat.Client.say server_pid, "Hi!"
Chat.Client.leave server_pid
end
# And another one
spawn fn() ->
Chat.Client.join server_pid
Chat.Client.say server_pid, "What's up?"
Chat.Client.leave server_pid
end
# By this time we have 6 notifications pending
# (corresponding to three 'notify_all' calls per each client)
Enum.times 6, fn() ->
Chat.Client.flush server_pid
end
$ elixir chat_demo.ex
Some user with pid <0.41.0> joined
Some user with pid <0.42.0> joined
<0.41.0>: Hi!
User with pid <0.41.0> left
<0.42.0>: What's up?
User with pid <0.42.0> left
@alco
Copy link
Author

alco commented Mar 28, 2012

For some reason I get a noticeable one-second delay before the program shuts down.

@josevalim
Copy link

@alco the delay is because the Erlang VM is properly shutting down. It for example checks the IO server to see if does not have any pending messages in its queue and so on. If you want a quick and dirty shut down, you can do it by calling halt.

@ngocdaothanh
Copy link

For consistency with the Getting Started Guide, should this sample be reindented to use 2 spaces?

@alco
Copy link
Author

alco commented Apr 14, 2012

Done. Thanks for pointing that out.

@drdaeman
Copy link

Code needs to be fixed a bit, there's a new syntax for receive: no more match: { ... }/after: 1000, but { ... } -> and after 1000 -> :)

(Rant: If there'd be some changelog on such things... There're lots of Elixir code samples floating around the 'net, but they're mostly broken.)

@josevalim
Copy link

josevalim commented May 27, 2012 via email

@alco
Copy link
Author

alco commented May 28, 2012

I'll leave the current version as it is for historical record.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment