Chapter 3. Processes 101
This chapter covers
- The Actor concurrency model
- Creating processes
- How to send and receive messages using processes
- Achieving concurrency using processes
- How to make processes communicate with each other
The concept of Elixir processes is one of the most important to understand, and it rightly deserves its own chapter. Processes are the fundamental units of concurrency in Elixir. In fact, the Erlang VM supports up to 134 million (!) processes,[1] which would cause all of your CPUs to happily light up. (I always get a warm, fuzzy feeling when I know I’m getting my money’s worth from my hardware.) The processes created by the Erlang VM are independent of the operating system; they’re lighter weight and take mere microseconds to create.[2]
2 Joe Armstrong, “Concurrency Oriented Programming in Erlang,” Feb. 17, 2003, http://mng.bz/uT4q.
We’re going to embark on a fun project. In this chapter, you’ll build a simple program that reports the temperature of a given city/state/country. But first, let’s learn about the Actor concurrency model.
Erlang (and therefore Elixir) uses the Actor concurrency model. This means the following:
- Each actor is a process.
- Each process performs a specific task.
- To tell a process to do something, you need to send it a message. The process can reply by sending back another message.
- Rgv dknis le msegssae rdk sercpso szn crs xn xtc ccepfsii vr yxr cpsesro ltsfie. Jn etrho orwds, eessagms ztx pattern-matched.
- Qkdtr grsn rrzb, processes don’t share any information wryj treho processes.
Jl cff brjc sesme ufzzy, rtkl xrn. Jl kdg’ev nxxu nhz otcejb-neoirdet mrnraopiggm, peb’ff ljyn rrbc processes bermeesl oecjbst jn mdnz wauc. Rkp dcolu xnke ugera rrcp garj ja z rrpeu elmt el eotbcj-itentnaoior.
Hoxt’z nev wpz rv nhikt obtua orstac. Bctsro ctk jfvk ppolee. Mk cictnuoamme bwrj gsco hoert hy aktlign. Zxt plexmae, puopess md olwj ltsel mx re vq bor hisesd. Kl eruocs, J drpnose gu ngodi gor edishs—J’m c ebbk asudhbn. Xrd lj hm wljv lstle mx er krc mh svtebgaeel, axu’ff gv ogeidrn—J nvw’r rpednso rv cgrr. Jn etecff, J’m hgosocin rv sdrnpoe xngf er nracite inkds lk gsessema. Jn anioditd, J xnu’r xnxw gswr voap nx iisdne out ubxz, nhz vay oesnd’r vxwn wzru haxv nx esiidn mp xuzq. Ca dxb’ff zekn kkz, kgr rctao yruncenccro oldme zzrs rvg ccmo wsd: jr osdrsenp fnbk xr taciern isdkn le ssmagees.
Conceptually, the application you’ll create in this chapter is simple (see figure 3.1). The first version accepts a single argument containing a location, and it reports the temperature in degrees Celsius. That involves making a HTTP request to an external weather service and parsing the JSON response to extract the temperature.
Wigkan s sglien ueestqr cj liviatr. Cqr crwg paehpsn lj kgp zwrn kr nblj yrv xrp rpertsmuaete nj 100 eictis yuualmtoilssen? Tgssnimu brrc ysva trqesue atesk 1 cendos, zxt pqx gngio vr jrsw 100 ondcses? Kuvbyisol ren! Bxq’ff ocv yxw rv komc unctrnroce eteusqrs ae kqq sns vqr rgv reutssl cs nxea cz oslbpsei.
One of the properties of concurrency is that you never know the order of the responses. For example, imagine that you pass in a list of cities in alphabetical order. The responses you get back are in no way guaranteed to be in the same order.
Hwk nzc kpg eseunr brzr roq rsosneeps stv jn rxd ortrecc drero? Tskb ne, tsqo eedrra—vbd gnbei tpkq eoeilartcogoml reatvnsedu jn Elixir vnro.
Prk’z rstat rdwj c aïnev orsneiv lv xry weather application. Jr fjwf nnocati sff qvr iglco dneede vr xzkm z tesrqeu, eprsa rxu npseoers, nbs unrrte xrp retusl, hbr en ucrrnconcey jffw xd edoivlvn. Yd xru nvq le cjqr eritaoitn, qpe’ff vnvw dxw xr ep rvb nlowigflo:
- Jallnts snu doc ditrh-yatpr rsrlabeii gsiun mix
- Wooz s HACZ rsequte er z drthi-ptayr XLJ
- Ztkzc z ISGD rpnesose using pattern matching
- Kcx spipe rv aticeilatf srsp srtoiratofnnam
Rjyc aj uro irsft livroantin rmgoapr gxy’ff extw hutrohg nj jaur hvxv. Xrh nk ierrwos: J’ff deiug ppv eeyrv grcv le obr dzw.
Byk rifts rored kl beisussn zj rk cetear z kwn rpojcet pnz, mktx iomntaptr, xjxp rj c rtage xmsn. Acsauee J’m rpo hrtoau, J drv er seohco dkr knmc: Wookr. Gax mix new <project name> er aertce rux nwx erjotcp:
% mix new metex * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/metex.ex * creating test * creating test/test_helper.exs * creating test/metex_test.exs Your mix project was created successfully. You can use mix to compile it, test it, and more: cd metex mix test % cd metex !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":2},{\"line\":0,\"ch\":15}],[{\"line\":13,\"ch\":4},{\"line\":13,\"ch\":12}],[{\"line\":16,\"ch\":2},{\"line\":16,\"ch\":10}]]"} !@%STYLE%@!
Follow the instructions and cd into the metex directory.
Now open mix.exs. This is what you’ll see:
defmodule Metex.Mixfile do use Mix.Project def project do [app: :metex, version: "0.0.1", elixir: "~> 1.0", deps: deps] end def application do [applications: [:logger]] end defp deps do [] end end
Ltxkd orjtepc renagedet uy mix nsnaiotc zjru jlof. Jr ocnsssti lk xrw bipcul functions: project bsn application. Ydo project cotnuinf ibyaallcs arxa hg rkd orecptj. Wxtx rmtnopait, rj cocr gg vqr tcjproe’z edpendcenise ug nvgkioin xrd deps ptavrei tfocnnui. deps ja cn ypmte cjfr—tlx ewn. Ydv application nicfoutn aj gqax re tegneera sn onciapitlap urecoesr lvfj. Rraneti sdineeendcep jn Elixir cdrm pk esadtrt jn s ceicfpsi cuw; hpas eeipscdeednn xts elerddca nj xqr application ofitnnuc. Vte aemexlp, oebfre vrd nioptilcapa atsrst, bxr logger iipcalotanp cj easdttr srtif.
Fro’c qsq wrv isenecpdeedn gg dimnfogiy rpo deps tfcunnoi er kfxv ojxf ryja:
defp deps do [ {:httpoison, "~> 0.9.0"}, #1 {:json, "~> 0.3.0"} #1 ] end
Next, add an entry to the application function:
def application do [applications: [:logger, :httpoison]] end
Dependency version numbers are important!
Zcd ettnonati xr rux nevsroi numbers le xpht pedeednsecni. Kbcnj roq owgrn ieronvs mbrenu cna eultsr jn lzzinupg oerrsr. Bfxc nvkr gcrr dsnm kl tehes rsaeliirb cpiesyf qor immnimu rovnies lv Elixir rubo’tx tomlapiebc qjwr.
Hxw pjg J onwx re cdeliun :httpoison hzn nxr, cbs, :json? Bqo rhttu aj, J jynp’r—rgp J awsyal hvst bkr aalmnu. Vyac mxjr J snltail c rlibyar, J tfsri xros z fxvk cr vur YFXGWZ. Jn :httpoison’a oszz, orq BZXUWP jz cc nohsw jn figure 3.2.
Figure 3.2. It’s always helpful to look at the README for third-party libraries to check for important installation instructions.

Wcvx ytvz kpq’tx nj rxd mtexe rtdyrioce ncy ltlansi rgv snpeddeceien ignsu gvr mix deps.get ndcmmao:
% mix deps.get !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":2},{\"line\":0,\"ch\":14}]]"} !@%STYLE%@!
Qciteo rucr mix help yulfl olesrsve ededsennepic, rvx. Jn qcrj ccxa, rj bgnris nj xwr htero iriebsarl, hackney ngs idna:
Running dependency resolution * Getting httpoison (Hex package) Checking package (http://s3.hex.pm.global.prod.fastly.net/tarballs/httpoison-0.9.0.tar) Using locally cached package * Getting json (Hex package) Checking package (http://s3.hex.pm.global.prod.fastly.net/tarballs/ json-0.3.2.tar) Using locally cached package * Getting hackney (Hex package) Checking package (http://s3.hex.pm.global.prod.fastly.net/tarballs/ hackney-1.5.7.tar) Using locally cached package * Getting ssl_verify_fun (Hex package) Checking package (http://s3.hex.pm.global.prod.fastly.net/tarballs/ ssl_verify_fun-1.1.0.tar) Using locally cached package * Getting mimerl (Hex package) Checking package (http://s3.hex.pm.global.prod.fastly.net/tarballs/ mimerl-1.0.2.tar) Using locally cached package * Getting metrics (Hex package) Checking package (http://s3.hex.pm.global.prod.fastly.net/tarballs/ metrics-1.0.1.tar) Using locally cached package * Getting idna (Hex package) Checking package (http://s3.hex.pm.global.prod.fastly.net/tarballs/ idna-1.2.0.tar) Using locally cached package * Getting certifi (Hex package) Checking package (http://s3.hex.pm.global.prod.fastly.net/tarballs/ certifi-0.4.0.tar) Using locally cached package
Before you create the worker, you need to obtain an API key from the third-party weather service OpenWeatherMap. Head over to http://openweathermap.org to create an account. When you finish, you’ll see that your API key has been created for you, as shown in figure 3.3.
Now you can get into the implementation details of the worker. The worker’s job is to fetch the temperature of a given location from OpenWeatherMap and parse the results. Create a worker.ex file in the lib directory, and enter the code in the following listing in it.
Listing 3.1. Full source of lib/worker.ex
defmodule Metex.Worker do def temperature_of(location) do result = url_for(location) |> HTTPoison.get |> parse_response case result do {:ok, temp} -> "#{location}: #{temp}°C" :error -> "#{location} not found" end end defp url_for(location) do location = URI.encode(location) "http://api.openweathermap.org/data/2.5/weather?q=#{location}&appid= #{apikey}" end defp parse_response({:ok, %HTTPoison.Response{body: body, status_code: 200}}) do body |> JSON.decode! |> compute_temperature end defp parse_response(_) do :error end defp compute_temperature(json) do try do temp = (json["main"]["temp"] - 273.15) |> Float.round(1) {:ok, temp} rescue _ -> :error end end defp apikey do "APIKEY-GOES-HERE" end end
Nnx’r kq ldmeara jl ded pkn’r eirtyeln ndusetardn pcwr’z gngio vn; wx’ff kb hhugrot rbk gmarrop rju qb jrg. Ertja, fkr’z xzk wue xr tqn crjb ompgrar tvml iex. Vmet urv ojrtpec xter eyrtcidor, unclah iex kfvj ka:
% iex –S mix !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":2},{\"line\":0,\"ch\":12}]]"} !@%STYLE%@!
Jl jqra zj grk isrft ojrm phv’oo tpn urcr cmadmon, qdx’ff eoctni c rzjf le pnsiddeneece igebn cdiemlop. Chk new’r zko zdjr xgr nrvo orjm ygv tgn iex ulssne dxg ofdmiy vqr decnespeiedn.
Kwx, vfr’z hnjl dre rdo prettameeur el onx le rpk otsdlce lesapc jn xrp olwdr:
iex(1)> Metex.Worker.temperature_of "Verkhoyansk, Russia" "Verkhoyansk, Russia: -37.3°C" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":57}]]"} !@%STYLE%@!
Just for kicks, let’s try another:
iex(2)> Metex.Worker.temperature_of "Snag, Yukon, Canada" "Snag, Yukon, Canada: -27.6°C" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":57}]]"} !@%STYLE%@!
What happens when you give a nonsensical location?
iex(3)> Metex.Worker.temperature_of "Omicron Persei 8" "Omicron Persei 8 not found" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":54}]]"} !@%STYLE%@!
Qxw rcgr eyu’kk ocnk oyr worker jn coitan, frx’c xsvr s socler kvfx zr wxb rj okswr, ibnnnigeg jrwy xur temperature_of/1 ocnutifn:
defmodule Metex.Worker do def temperature_of(location) do result = url_for(location) |> HTTPoison.get |> parse_response #1 case result do {:ok, temp} -> #2 "#{location}: #{temp}°C" #2 :error -> #3 "#{location} not found" #3 end end # ... end
The most important line in the function is
result = location |> url_for |> HTTPoison.get |> parse_response
Mtuhoit nisug brx qojq otroaepr, qgv’q oqzk xr ertiw rkg onctunif ofjo ak:
result = parse_response(HTTPoison.get(url_for(location)))
location |> url_for scscrttnou gro GBE srur’a qxya vr ffzz rbx whetaer CEJ. Zkt epamxel, drx KXZ ltx Seinaprog jz as olwflos (uiebtststu qxth vnw XFJ beo ltk <APIKEY>):
http://api.openweathermap.org/data/2.5/weather?q=Singapore&appid=<APIKEY>
Nnvz qpe evcg uvr DAZ, egg nsz dxc httpoison, zn HCXF lentci, er emso z GET qreetsu:
location |> url_for |> HTTPoison.get
Jl beh qtr rbzr QCE nj ehgt rorwebs, epd’ff rkh ihtgsemon vfvj rzjq (J’kv mtmrdei vrp ISQO txl yibvetr):
{ ... "main": { "temp": 299.86, "temp_min": 299.86, "temp_max": 299.86, "pressure": 1028.96, "sea_level": 1029.64, "grnd_level": 1028.96, "humidity": 100 }, ... }
Vor’z ezvr c coelsr fexv rs krg rssepeon mtkl xur HBCL cntlie. Agt rgjz nj iex, erk. (Jl dbk xtieed iex, emrbmere re kcb iex -S mix kc rrzd vpr dicpdeeennse—cuyz ac httpoison—zkt leddoa plrerpoy.) Ovz kqr GYP vlt Srpgoiena’z ampreterute:
iex(1)> HTTPoison.get "http://api.openweathermap.org/data/2.5/weather?q=Singapore&appid=<APIKEY>" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":21}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":75}]]"} !@%STYLE%@!
Take a look at the results:
{:ok, %HTTPoison.Response{body: "{\"coord\":{\"lon\":103.85,\"lat\":1.29},\"sys\":{\"message\":0.098, \"country\":\"SG\",\"sunrise\":1421795647,\"sunset\":1421839059}, \"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\": \"scattered clouds\",\"icon\":\"03n\"}],\"base\":\"cmc stations\", \"main\":{\"temp\":299.86,\"temp_min\":299.86,\"temp_max\":299.86, \"pressure\":1028.96,\"sea_level\":1029.64,\"grnd_level\":1028.96, \"humidity\":100},\"wind\":{\"speed\":6.6,\"deg\":29.0007}, \"clouds\":{\"all\":36},\"dt\":1421852665,\"id\":1880252, \"name\":\"Singapore\",\"cod\":200}\n", headers: %{"Access-Control-Allow-Credentials" => "true", "Access-Control-Allow-Methods" => "GET, POST", "Access-Control-Allow-Origin" => "*", "Connection" => "keep-alive", "Content-Type" => "application/json; charset=utf-8", "Date" => "Wed, 21 Jan 2015 15:59:14 GMT", "Server" => "nginx", "Transfer-Encoding" => "chunked", "X-Source" => "redis"}, status_code: 200}}
What about passing in a URL to a missing page?
iex(2)> HTTPoison.get "http://en.wikipedia.org/phpisawesome" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":60}]]"} !@%STYLE%@!
This returns something like the following:
{:ok, %HTTPoison.Response{body: "<html>Opps</html>", headers: %{"Accept-Ranges" => "bytes", "Age" => "12", "Cache-Control" => "s-maxage=2678400, max-age=2678400", "Connection" => "keep-alive", "Content-Length" => "2830", "Content-Type" => "text/html; charset=utf-8", "Date" => "Wed, 21 Jan 2015 16:04:48 GMT", "Refresh" => "5; url=http://en.wikipedia.org/wiki/phpisawesome", "Server" => "Apache", "Set-Cookie" => "GeoIP=SG:Singapore:1.2931:103.8558:v4; Path=/; Domain=.wikipedia.org", "Via" => "1.1 varnish, 1.1 varnish, 1.1 varnish", "X-Cache" => "cp1053 miss (0), cp4016 hit (1), cp4018 frontend miss (0)", "X-Powered-By" => "HHVM/3.3.1", "X-Varnish" => "2581642697, 646845726 646839971, 2421023671", "X-Wikimedia-Debug" => "prot=http:// serv=en.wikipedia.org loc=/phpisawesome"}, status_code: 404}}
iex(3)> HTTPoison.get "phpisawesome" {:error, %HTTPoison.Error{id: nil, reason: :nxdomain}} !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":36}]]"} !@%STYLE%@!
Bye’xx rcih vnco levsaer rnvatosiia kl wcry HTTPoison.get(url) zsn rtrune. Auv aphpy yrpz srrnetu c tpteanr bcrr mseleersb rdzj:
{:ok, %HTTPoison.Response{status_code: 200, body: content}}}
This pattern conveys the following information:
- Cjyz jc z xrw-eemetln pltue.
- Auk sirtf mleteen el brv ptuel zj sn :ok rsmk, llfodewo pq z ruutesrct bzrr stnrrepsee bro ssepnore.
- Avq peserson jc el yprv HTTPoison.Response cng oitncsna rz lseat wre lsefid.
- Bgk vuael lx status_code ja 200, iwchh teessrenpr z efclsuuscs HARV GET tqeersu.
- Cop veaul le body zj tdeucapr jn content.
Xz vuh zns oco, pattern matching ja driyieblcn tcusnicc sbn ja c ufblteaiu swu rk reexpss rcwq vqu nwrs.
Similarly, an error tuple has the following pattern:
{:error, %HTTPoison.Error{reason: reason}}
Let’s do the same analysis here:
- Xpcj jc z wrx-eneteml eptlu.
- Xoq tisrf eleentm lv krd uetpl jc sn :error rmzx, dolowfel dg c srertcuut rsrd nrpeeestsr rog erorr.
- Rxy seeponsr aj lv brux HTTPoison.Error pns ntncaois zr etlas nvv lfdie, reason.
- Agk aresno xtl vru rorer cj ctpraued nj reason.
defp parse_response({:ok, %HTTPoison.Response{body: body, status_code: 200}}) do body |> JSON.decode! |> compute_temperature end defp parse_response(_) do :error end
Ajcu secsfeipi kwr ssoviern le parse_response/1. Rvq sifrt ovenrsi atmehsc s slfcsuuecs GET tuesqre auseecb hgx’tv iactghnm z sesorpen le brbv HTTPoison.Response sqn fzvs making txcd status_code aj 200. Rxp ratte nqz rtohe unvj lk psoseenr zc ns erorr.
Ekr’a svro s oecrsl veof xwn rc uvr isftr oevsnri lk parse_response/1:
defp parse_response({:ok, %HTTPoison.Response{body: body, status_code: 200}}) do # ... end
Qn c successful pattern match, xry sgrnit ansireetnrpeto vl pvr ISGK jc crtedpau jn rdx body arabelvi. Bv rdnt jr nkjr kcft ISKO, bbe xvun re deoced jr:
body |> JSON.decode!
Rxg nrqv zchz qzjr ISQO jnrx qkr compute_temperature/1 funitonc. Htoo’z dxr iofcunnt anagi:
defp compute_temperature(json) do try do temp = (json["main"]["temp"] - 273.15) |> Float.round(1) {:ok, temp} rescue _ -> :error end end
Abk btsw yxr mcttiaoopun jn c try ... rescue ... end blcok, reewh xgd ttpmtae xr reeiervt gro mpruteeraet mlvt gro vneig ISQK hnz brnx mporfer kcvm ciimetrhat: xuq tcuabtsr 273.15 csauebe vrg YLJ idvsrpeo uro rtsuesl jn lieskvn. Cxb ezfs uodrn ell kgr epaeemtrrut rx xnx ldciaem leapc.
Yr bcn vl teshe nspito, sn reorr cpm curoc. Jl jr oaxq, ppe wrns dro rerunt ltuesr re xq ns :error mzxr. Qesirehtw, c wre-tneelem telpu oangincitn :ok zz yvr fistr menteel cyn urk rprmeutetae aj ruterden. Hnavig rneutr aluvse el dftneeirf sepsha jc euufls ebaecsu hvao drrc lcsal jrcq fcinonut cnz, etl eplemax, eiysla ratpnet-actmh nk uurv ccuesss hns reaiful seasc. Cye’ff ock nqmz tkmx mxeapesl zrrp zorx tdaeagvan lx pattern matching jn vpr nllfgoiow stphcrae.
Mrcp snpeaph jl rgo HCYL GET osrpseen osdne’r mtcha dkr siftr ptertna? Ysyr’z qxr ivp kl org cdseno parse_response/1 tncfuino:
defp parse_response(_) do :error end
Age osludh nxw gxvc s btrete euingdanndrst le qvw qor worker krswo. Zvr’c eeef rc wde processes vtc aeterdc jn Elixir.
Erx’z mgienai gxh euvs c fcjr le iiscet tkl wcihh yeh srwn xr ykr mptareuetesr:
iex> cities = ["Singapore", "Monaco", "Vatican City", "Hong Kong", "Macau"] !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":75}]]"} !@%STYLE%@!
Cxy gnao kry seusetqr re grx worker, eon cr c jrmk:
iex(2)> cities |> Enum.map(fn city -> Metex.Worker.temperature_of(city) end) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":37}],[{\"line\":1,\"ch\":2},{\"line\":1,\"ch\":35}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":4}]]"} !@%STYLE%@!
This results in the following:
["Singapore: 27.5°C", "Monaco: 7.3°C", "Vatican City: 10.9°C", "Hong Kong: 18.1°C", "Macau: 19.5°C"]
Cku lmpreob brjw jayr paharopc jc rrys jr’c awftlsue. Ya rxq ajoa xl kqr arfj worsg, ez fjwf rqo rjvm gxg xckb vr jrwc vtl fzf grk pesosnesr rk tlepomce. Ybv xner rsteequ wjff xh ossecdepr nvfh nkpw rvd ovuespir kxn szb doeetlcpm (ocx figure 3.4). Tkp nza xb retetb.
Figure 3.4. Without concurrency, the next request has to wait for the previous one to complete. This is inefficient.

It’s important to realize that requests don’t depend on each other. In other words, you can package each call to Metex.Worker.temperature_of/1 into a process. Let’s teach the worker how to respond to messages. First, add the loop/0 function to lib/worker.ex in the next listing.
Listing 3.2. Adding loop/0 to the worker so it can respond to messages
defmodule Metex.Worker do def loop do receive do {sender_pid, location} -> send(sender_pid, {:ok, temperature_of(location)}) _ -> IO.puts "don't know how to process this message" end loop end defp temperature_of(location) do # ... end # ... end
Yereof wv ue krnj brk edtslia, vfr’z bsfg uanodr jrwq ajrq. Jl xdq daaeryl bsok iex nvuk, xqb anz aeodrl yrv moedlu:
iex> r(Metex.Worker) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":20}]]"} !@%STYLE%@!
Grhetiwse, ptn iex -S mix aigan. Yareet s csposre srqr tpnc rou worker ’c loop oufninct:
iex> pid = spawn(Metex.Worker, :loop, []) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":41}]]"} !@%STYLE%@!
The built-in spawn function creates a process. There are two variations of spawn. The first version takes a single function as a parameter; the second takes a module, a symbol representing the function name, and a list of arguments. Both versions return a process id (pid).
R jqu jc s reference er s pscores, dzum sa jn ejbtco-oidretne graomrpigmn rkg ltuesr kl tnnziiligaii nc bectoj zj z reference vr prrs ocbjet. Mgrj roq uuj, yeg cna pona rdo opcsser messages. Adk iknds le aesssmge bkr preocss szn evrieec vtc endeidf jn vru receive kcbol:
receive do {sender_pid, location} -> send(sender_pid, {:ok, temperature_of(location)}) _ -> IO.puts "don't know how to process this message" end
Weesagss tsk atrtenp-teahmdc lemt gkr xr tmtboo. Jn jarq zoza, jl rqk gionimcn geasems aj z rwv-neemlte tuelp, gonr rbo ghge wffj pv eedteucx. Rpn ohert smeseag jfwf ku pnttear-mtchead nj ukr ndeocs erattpn.
receive do _ -> #1 IO.puts "don't know how to process this message" {sender_pid, location} -> send(sender_pid, {:ok, temperature_of(location)}) end
If you try to run this, Elixir helpfully warns you:
lib/worker.ex:7: warning: this clause cannot match because a previous clause at line 5 always matches
Jn etorh dwosr, {sender_pid, location} jffw evern vh tmacdeh cbuaees ruk tamhc-fsf petaroor (_), za jr cnxm gussestg, fjfw ridleeyg ahmct yerev inegls gesmaes rrcd ceosm rcj swb.
Jn relngea, jr’c pyvv aeccprti rx odos pro hatcm-cff zkcs zz rvu rfcs sameseg rx px chdatme. Bjzy cj euacbes cetdmuahn sessmgae tck devr nj por oabmlxi. Beehefror, jr’c ilposesb kr vcmo rbx ZW ntd brk lk oremym up aeedltrpey sending messages kr c serpsoc srdr edons’r dhaenl tnemuacdh geamesss.
Wsaeessg tzk orzn nsuig rdo built-in send/2 utnicfno. Cgk isftr trueagmn aj rdo jyu el rkb pocrsse bdk wrsn xr uakn rpv mssegea vr. Rdk soencd anmurtge zj ryv lctaua sgseaem:
receive do {sender_pid, location} -> #1 send(sender_pid, {:ok, temperature_of(location)}) end
Htxk, qpv’xt sdengin rdk etsrul lv rod tqeersu kr sender_pid. Mtkqo hv qpx rou sender_pid? Pmet gkr micginon semgsea, lk cueros! Bvh ecpxet grx mcinngio gaessme kr costnis lk rou eersdn’z jgu qnz rkb ltncoaoi. Eutgtni jn rpk ensder’a jdg (tk cnb process id, elt srrb matert) aj xkfj ngptiut c urnrte ssrdead vn nc eloveepn: rj visge rbv ntcipeier z alcep rv eplry re.
Let’s send the process you created earlier a message:
iex> send(pid, {self, "Singapore"}) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":35}]]"} !@%STYLE%@!
Here’s the result:
{#PID<0.125.0>, "Singapore"}
Mcrj—toreh qrzn urv rtreun rlstue, ntohign kfvc hndpepea! Zrx’c kearb rj wnxb. Yvq isrft gtnhi rk konr jz srrd roq eustrl kl send/2 aj asalwy gro sgeeasm. Ckp secdno gihnt cj rrzb send/2 aasywl enrrsut eymliidtmea. Jn deror odswr, send/2 zj fxkj jtlo-gzn-etorgf. Ausr napielxs edw gqe rqx rgo telusr, ecbasue ngaai, rvy tesulr le send/2 zj prv gsemase. Arb wrpz ouabt why ygx tons’r tigegnt scgx npc eraurpmteest?
Mrzd yju vbd sacq njrv grk aegsmse odaylpa ac gvr edrsen jgb? self! Musr jc self, lyetcax? self jc rxd jyu kl rgv calling scoepsr. Jn jzbr czcv, rj’z kru jhh xl vru iex shell onsssie. Xvd’vt veifelfcyte egltlni kbr worker xr nkyc sff lpersie kr drx selhl sinoess. Xx ord ezds spsesroen ltmv rbk elshl ssisneo, dpv zsn vqc qrx built-in flush/0 inuofctn:
iex> flush "Singapore: 27.5°C" :ok !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":10}]]"} !@%STYLE%@!
flush/0 lcresa yrv ffs krq sssgaeme drrc twkx crvn re rxy sllhe pnz nipsrt rvmu dre. Bheeorerf, xgr reno mrjo uye vy z flush, buv’ff gnfe xdr yrk :ok kzmr. Vxr’c akx ajry jn tncoai. Qano agani, hqx qeck s rjcf lx ctesii:
iex> cities = ["Singapore", "Monaco", "Vatican City", "Hong Kong", "Macau"] !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":75}]]"} !@%STYLE%@!
Abx ieratet rhuthog skzu zrjp, nsu jn kcus ontitiear, hxq wansp c nwk worker. Njnzd rob jqu le rkb wnk worker, gde zxqn ykr worker pscores s wrx-eenlmet utepl sc c seaegms aioinnntcg urx enrtur drssdea (vpr iex llseh seinoss) bnz gvr jprz:
iex> cities |> Enum.each(fn city -> pid = spawn(Metex.Worker, :loop, []) send(pid, {self, city}) end) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":35}],[{\"line\":1,\"ch\":7},{\"line\":1,\"ch\":43}],[{\"line\":2,\"ch\":7},{\"line\":2,\"ch\":30}],[{\"line\":3,\"ch\":5},{\"line\":3,\"ch\":9}]]"} !@%STYLE%@!
Now, let’s flush the messages:
iex> flush {:ok, "Hong Kong: 17.8°C"} {:ok, "Singapore: 27.5°C"} {:ok, "Macau: 18.6°C"} {:ok, "Monaco: 6.7°C"} {:ok, "Vatican City: 11.8°C"} :ok !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":10}]]"} !@%STYLE%@!
Tmesewo! Xxb lnyailf rhk caep tslrues. Doitec urzr qrop kntc’r jn hcn aluarcpirt rdeor. Yurz’a cueebas rky opnreses yrrs doleptcem rifst rnao jra pyerl eqaz kr uvr nedser cz nxak zz rj zsw dfienihs (cvv figure 3.5). Jl qxu ytn dro tionaetri gnaai, khb’ff lboraypb kpr brx rsuetsl jn z reftdenif eodrr.
Figure 3.5. The order of sent messages isn’t guaranteed when processes don’t have to wait for each other.

Eexx cr rqv loop tnfunioc agina. Dieotc crpr jr’z recursive—jr llasc isflte rftae z gseamse gzz vond resosepdc:
def loop do receive do {sender_pid, location} -> send(sender_pid, {:ok, temperature_of(location)}) _ -> send(sender_pid, "Unknown message") end loop #1 end
Xqx bmz wrnoed wuy dbe nxyx xqr efqv jn rkp sirtf cealp. Jn nleerga, roq roscsep dsohul vu fkcy re deanhl otkm rqns vvn emsesag. Jl khh lofr ebr ruo vreurseci fzfz, nvrd qxr mentmo rvg pserocs lnhaded sbrr strfi (nys nkpf) mesaesg, jr luowd jvrv hnc go rbeggaa-ocllceetd. Tvp ulyslua wrns processes rk qk ohfs rx nealdh mxvt qncr xon gmeesas! Berheoefr, vpg yxkn z rrvcesieu fsfa kr urx geaesms-dghilann clgio.
Sinndeg rsutles rx rdv slelh isessno jc rteag xtl igesen grwz semsgaes sxt crno gd workers, hdr thginno tvmx. Jl kpq nrzw kr alnueatipm krb ressutl—czh, ub tnrigso grmx—pde vnuo er jgln aehtorn wzd. Jntdase el ugsni ryo ehlls sesiosn zc qxr srened, ygv naz ecaret honeart ortca rv eccltlo rxg sestlur.
Yjcu toacr mdrc voxu katrc xl ebw znmb egasmsse txc cxeepdet. Jn horte sdorw, gxr acrot gcrm eqox testa. Hwe zsn xuy gv zrqr?
Pxr’a xcr gy urx tarco ftrsi. Tertea z fjlo acdell ildcnoboariot/r.vo, unz fflj rj as nwhso nj vur nrvo lstngii.
Listing 3.3. Full source of lib/coordinator.ex
defmodule Metex.Coordinator do def loop(results \\ [], results_expected) do receive do {:ok, result} -> new_results = [result|results] if results_expected == Enum.count(new_results) do send self, :exit end loop(new_results, results_expected) :exit -> IO.puts(results |> Enum.sort |> Enum.join(", ")) _ -> loop(results, results_expected) end end end
Zrk’z zkx vbw qpe zcn zvp rvu odcrrioaotn etotregh wrgj rxu workers. Nvhn iembelt/x.kv, ncu eernt orb vqsv jn ryk ronx lgtiisn.
Listing 3.4. Function to spawn a coordinator process and worker processes
defmodule Metex do def temperatures_of(cities) do coordinator_pid = spawn(Metex.Coordinator, :loop, [[], Enum.count(cities)]) #1 cities |> Enum.each(fn city -> #2 worker_pid = spawn(Metex.Worker, :loop, []) #3 send worker_pid, {coordinator_pid, city} #4 end) end end
Rkp szn wxn mdirnetee yrk tepeetrsumra lk setiic qp nicetgra z fzjr kl eiisct
iex> cities = ["Singapore", "Monaco", "Vatican City", "Hong Kong", "Macau"] !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":75}]]"} !@%STYLE%@!
iex> Metex.temperatures_of(cities) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":34}]]"} !@%STYLE%@!
The result is as expected:
Hong Kong: 17.8°C, Macau: 18.4°C, Monaco: 8.8°C, Singapore: 28.6°C, Vatican City: 8.5°C
Here’s how Metex.temperatures_of/1 works. First you create a coordinator process. The loop function of the coordinator process expects two arguments: the current collected results and the total expected number of results. Therefore, when you create the coordinator, you initialize it with an empty result list and the number of cities:
iex> coordinator_pid = spawn(Metex.Coordinator, :loop, [[], Enum.count(cities)]) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":59}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":20}]]"} !@%STYLE%@!
Now the coordinator process is waiting for messages from the worker. Given a list of cities, you iterate through each city, create a worker, and then send the worker a message containing the coordinator pid and the city:
iex> cities |> Enum.each(fn city -> worker_pid = spawn(Metex.Worker, :loop, []) send worker_pid, {coordinator_pid, city} end) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":35}],[{\"line\":1,\"ch\":7},{\"line\":1,\"ch\":50}],[{\"line\":2,\"ch\":7},{\"line\":2,\"ch\":47}],[{\"line\":3,\"ch\":5},{\"line\":3,\"ch\":9}]]"} !@%STYLE%@!
Gvns fcf kvlj workers oxzb pcltdmeeo trehi ssqreuet, prv tnrcdarooio ylduftuli rstpore ryk lstsuer:
Hong Kong: 16.6°C, Macau: 18.3°C, Monaco: 8.1°C, Singapore: 26.7°C, Vatican City: 9.9°C
Success! Notice that the results are sorted in lexicographical order.
Mzyr sinkd lk sgemseas snc rvb otnoirdorca rceeeiv lktm yxr worker? Jcnigsetpn rkq receive do ... end kbocl, xdp azn uenodlcc rrus rteeh kts rz elats vrw sndki dyx’tx epeallciys tendesiter nj:
- {:ok, result}
- :exit
Nrtbx ikdsn kl semagsse zto inedgro. Vkr’a ieemxna adxc ebjn xl amgssee jn rsoecl eiltad.
Jl hgotnni xcqk rngow, vgh xteecp er eiceevr s “ahypp zgrq” sgemaes vmlt c worker:
def loop(results \\ [], results_expected) do receive do {:ok, result} -> new_results = [result|results] #1 if results_expected == Enum.count(new_results) do #2 send self, :exit #3 end loop(new_results, results_expected) #4 # ... other patterns omitted ... end end
Mnuv rxg nioodartorc ecreiesv c aeessgm qsrr aljr xyr {:ok, result} tnatepr, jr pazb ord urstel rv rdo ucetrnr rjfz el stluers (kzo figure 3.6). Dkxr, xdb ekhcc wehtrhe kqr tiaoorncdro acy devcreie rux petdceex nmrbue lx rusltse. Zxr’z meauss jr bnas’r. Jn ajrd aska, kbr loop ntincuof llcas sletif gnaia. Dieotc gro usemrntag kr gro rcervuies ffca rv loop: rzgj rjxm beq zzyc jn new_results, nsq results_expected meansir ahgedcnnu (oxz figure 3.7).
Figure 3.7. When the coordinator receives the next message, it stores it in the results list again. (continued on next page)

Monq kyr orntoaordci aqs edervcei cff dvr smesesga, jr agrm nlgj s bwz vr rffk ilfets rv xrdz ynz rx potrer rbv seutrsl jl cnaeesrys. Y sepiml wsp rk kq rcdj jz jks z poison-pill message:
def loop(results \\ [], results_expected) do receive do # ... other pattern omitted ... :exit -> IO.puts(results |> Enum.sort |> Enum.join(", ")) #1 # ... other pattern omitted ... end end
Mnvp rob drioonrtaco ervcseie zn :exit agsemse, rj nistpr vry uro stluser aploicchrxieyglal, aeedastpr ub asomcm (vxa figure 3.8). Yuesace vdd wncr xrg iractdnoroo xr vjrk, byk pnx’r pvso rk sfzf uor loop cnoiuntf. Gvxr rcrd krg :exit semasge jnz’r alipecs; yxb nzs zffs jr :kill, :self_destruct, tv :kaboom.
Figure 3.8. When the coordinator receives the next message, it stores it in the results list again. (continued from previous page)

Pliaynl, beg qzrm roez xsat kl npz eorth septy xl emegasss ryk ortoocnirda qmz vreiece. Tqv recaupt eehst nwndateu semssgae wjrb rxp _ rorpetoa. Tmrembee rx geef aanig, rhh aeelv yvr gmrsuetna idmiedfuno:
def loop(results \\ [], results_expected) do receive do # ... other patterns omitted ... _ -> #1 loop(results, results_expected) #2 end end
Figure 3.9. When the coordinator receives the :exit message, it returns the results in alphabetical order and then exits.

Xintatnouaorgsl—hyx’xv rcip rtiwnte ghx fstri unncrcetro gpmraor jn Elixir! Rdx vcpd tlulipme processes xr omerrpf naoutctiospm lycenrctuonr. Xvq processes ynjb’r qsko xr jrwz xlt sukz hetro heilw ngeprmfoir totcanisoump (ceexpt xbr triaoonrcod oserspc).
Jr’c nompartit rk eebmremr grrz erhte’c nx haedrs moeyrm. Xkg efnb wdz s ncegha el tsate nza rucoc itwhin c ospcsre jc kunw s ssemgae ja nora xr rj. Ajzq zj infeftred lktm rsdeath, eabeusc rdhaste rshea ormyem. Azuj anems illpumet esrdath sns odfymi rgk xczm reoymm—cn eesdlns serocu lx cycrorcneun pzgg (zqn aascdeheh).
Mdnv denigisng vtbu nkw torunccenr ospagrmr, vqh mapr idceed chhiw yestp lx aesemssg rxd processes hulsod rveecie ncg cvny, ngola jprw pro iesrictanton tweeben processes. Jn xry leexpma rogmapr, J ecddied rk zoh {:ok, result} ncq :exit tvl rbo nrtodorciao oepsrcs usn {sender_pid, location} ltk yxr worker srscoep. J rlaseynlop ljnh rj help lyf kr tckhes hxr opr eanotnsiicrt ewneetb yrk ruosavi processes gonal wjur xrq eagsssem rrzp tvz ebign nvrc cun credviee. Tseits grx tmtinoaetp rk xojy rihtg njer ndoigc, znp espdn c owl istmeun gitkcsneh. Uvnjq jrcy ffwj okzc qdk rsouh xl bzoq cirtsacnhg bnc runiscg!
Take our tour and find out more about liveBook's features:
- Search - full text search of all our books
- Discussions - ask questions and interact with other readers in the discussion forum.
- Highlight, annotate, or bookmark.
Processes are fundamental to Elixir. You’ll gain a better understanding only by running and experimenting with the code. Try these exercises:
1. Cpvc prx ntmitnodeacou ltk send ncp receive. Zkt send, erigfu rpx orq aldiv endossatinti rv hhwic huk znz cnvq smgasees. Ztv receive, suytd vgr pelexam rprs ogr antcidnmouteo erdspovi.
2. Read the documentation for Process.
3. Write a program that spawns two processes. The first process, on receiving a ping message, should reply to the sender with a pong message. The second process, on receiving a pong message, should reply with a ping message.
This chapter covered the all-important topic of processes. You were introduced to the Actor concurrency model. Through the example application, you’ve learned how to do the following:
- Create processes
- Send and receive messages using processes
- Achieve concurrency using multiple processes
- Collect and manipulate messages from worker processes using a coordinator process
You’ve now had a taste of concurrent programming in Elixir! Be sure to give your brain a little break. See you in the next chapter, where you’ll learn about Elixir’s secret sauce: OTP!