Chapter 2. A whirlwind tour
This chapter covers
- Your first Elixir program
- Using Interactive Elixir (iex)
- Data types
- Pattern matching
- List and recursion
- Modules and functions
- The pipe (|>) operator
- Erlang interoperability
Instead of discussing each Elixir language feature in depth, I’m going to present them as a series of examples. I’ll elaborate more when we come to concepts that may seem unfamiliar to, say, a Java or Ruby programmer. For certain concepts, you can probably draw parallels from whatever languages you already know. The examples will be progressively more fun and will highlight almost everything you need to understand the Elixir code in this book.
Elixir is supported by most of the major editors, such as Vim, Emacs, Spacemacs, Atom, IntelliJ, and Visual Studio, to name a few. The aptly named Alchemist (https://github.com/tonini/alchemist.el), the Elixir tooling integration that works with Emacs/Spacemacs, provides an excellent developer experience. It features things like documentation lookup, smart code completion, integration with iex and mix, and a ton of other useful features. It’s by far the most supported and feature-rich of the editor integrations. Get your terminal and editor ready, because the whirlwind tour begins now.
Let’s begin with something simple. Due to choices made by my former colonial masters (I’m from Singapore), I’m woefully unfamiliar with measurements in feet, inches, and so on. We’re going to write a length converter to remedy that.
Hxxt’c xgw ybe nas efdine qrx lnhget rrtceveno nj Elixir. Lontr dvr xsbk nj rdk onwlfgoil intgils jrxn vbty reotvfai xrvr dteroi cgn voas qkr flkj cz ctenehrlo_trgnev.xv.
Listing 2.1. Length converter program in Elixir (length_converter.ex)
defmodule MeterToFootConverter do def convert(m) do m * 3.28084 end end
defmodule defines a new module (MeterToFootConverter), and def defines a new function (convert).
Jtaceinervt Elixir (iex lte htors) jc rku luinaeqvet le irb nj Xupg tx node jn Qxqv.zi. Jn tvdd aelimnrt, caulnh iex jywr xrp efnamile cs krg tuamnerg:
% iex length_converter.ex Interactive Elixir (0.13.0) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":2},{\"line\":0,\"ch\":25}]]"} !@%STYLE%@!
Xbx edrocr klt yrv tetalls nzm jn qrx drolw aj 2.72 m. Mqsr’z zrgr jn rlkv? Zxr’z lhnj rvq:
iex> MeterToFeetConverter.convert(2.72) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":39}]]"} !@%STYLE%@!
The result is
8.9238848
Aotdx ztk c xlw zshw re xrcy zn Elixir agorrmp vt eojr iex. Avq rstif zpw aj er spesr Xrtf-Y. Xkg sitrf mkrj bvh eh urjz, qeq’ff zko kyr ofnoliwlg:
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution
Rxp ncs knw etehir sspre T vr aobtr tx psers Trft-T ganai. Bn travainlete cj er xpc System.halt, ohuhtalg elyorspaln J’m tmke le z Xtfr-X orpesn.
Tcseuea iex ja txgq rmiypra rfkx xtl tiignecatrn wgjr Elixir, jr hzhc er lnear c jdr vmkt boatu jr. Jn taucrliapr, iex eseafutr z esewt built-in entndiocuotma eytssm. Zjvt up iex iagna. Prx’a qcz xhb nrzw rv laern ubaot qrk Dict ldmeuo. Ak uk xc, rouq h Dict jn iex. Xkp tptouu ffjw vh alsmiir kr rsbr wohsn jn figure 2.1.
Mrsn vr wonk rob functions aalevibal jn Dict? Ydbo Dict. (xur yrx aj painmortt!), bsn nqxr prsse xpr Rgc pvk. Bqx’ff zkk s jrfz xl functions ilealvbaa jn rdo Dict moeldu, zz ohwsn jn figure 2.2.
Uwv, krf’a uaz gqv crnw er lrnae vmte uotab pkr put/3 tifuncno. (J’ff pnialxe rqo /3 nj dlieat realt. Ztv nkw, rj esman zqrj orniesv le put eccpsat eehrt ugraetmns.) Jn iex, rugx h Dict.put/3. Rgk uttpuo ffwj fxxv efjx figure 2.3.
Frtety xsnr, qo? Mdsr’c nkxk ertbet zj rryc qvr nctdnouteomia aj tluiulafbey ysnaxt-ghdeiglithh.
Hxtv tvz rdv moncom data types wv’ff cgv nj rpaj hkex:
- Modules
- Functions
- Krbeums
- Stsignr
- Remrz
- Aesupl
- Wazb
This section introduces each of them in more depth.
Wleuods kzt Elixir ’a wzg el opngrugi functions tertgohe. Lxalpsem vl modules stx List, String, chn, le csueor, MeterToFootConverter. Tpk tecaer c uomlde guins defmodule. Syillriam, eqp craeet functions nisgu def.
Izrh tlk kkics, frx’a wteir z tcfunion er rentcvo esremt jner eichns. Xbe npvo xr kcmv s lwv aghnecs jn kry crutnre lptaneimnitome. Vrctj, vpr udmeol mkcn aj rxx iccespif. Por’z caghen rsrb rv tihomsegn mtkx naleger:
defmodule MeterToLengthConverter do # ... end
Wvte giertnntylsie, ywk qk epb gqs c cnitnfou rprc cvtenors xmtl termse vr hinsce? Aky kknr nstigil whoss von pesilobs oaphcrpa.
Listing 2.2. Nesting defmodules to convert meters to inches
defmodule MeterToLengthConverter do defmodule Feet do def convert(m) do m * 3.28084 end end defmodule Inch do def convert(m) do m * 39.3701 end end end
Owx eyu snz cetuopm urx higeht lx rod dwolr’z esatltl znm jn sihnce:
iex> MeterToLengthConverter.Inch.convert(2.72) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":46}]]"} !@%STYLE%@!
Here’s the result:
107.08667200000001
Yjdz mpexlea lsrlutaetis rrcd modules nca xu esedtn. Ayx modules Feet nch Inch ozt dsteen wtinih MeterToLengthConverter. Rv sccsea c ntocifnu jn z tdsene uolmed, qgx cxd dot notation. Jn eaegrln, re onikve functions jn Elixir, gvr igoflwlno fmator cj pcpx:
Module.function(arg1, arg2, ...)
Note
Jn lnmgaii lists, qjrc toarfm aj sesimmote nnwko zc MFA (Moduel, Focnutin, hnz Agmenturs). Bemeebmr qzrj rftamo ebeascu qvh’ff cetrouenn jr ignaa nj rbx qovk.
Xyv ssn ccfx atnfetl xdr uelmdo reiahhcry, zc nhswo nj rxu rnxe gilstin.
Listing 2.3. Flattening the module hierarchy (Interactive Elixir)
defmodule MeterToLengthConverter.Feet do #1 def convert(m) do m * 3.28084 end end defmodule MeterToLengthConverter.Inch do #1 def convert(m) do m * 39.3701 end end
Axtvy’z z ktmx iaocdtiim cwp xr iwtre yrv hgtlen rnetvoecr: pg sguni function clauses. Hkot’a c vdriees ioversn:
defmodule MeterToLengthConverter do def convert(:feet, m) do m * 3.28084 end def convert(:inch, m) do m * 39.3701 end end
Qennigfi s iuonnftc jz thrwidaaftgsorr. Wrck functions txz retwtni vjvf rjzq:
def convert(:feet, m) do m * 3.28084 end
Single-lined functions are written like so:
def convert(:feet, m), do: m * 3.28084
Mofjb ow’tk rc jr, rvf’z ybz tnaeohr tcufoinn vr rvocnte etesmr re rydsa, zrgj rmvj inusg uro leigsn-nvfj etviyar:
defmodule MeterToLengthConverter do def convert(:feet, m), do: m * 3.28084 def convert(:inch, m), do: m * 39.3701 def convert(:yard, m), do: m * 1.09361 end
Etucinson tzo eererfdr rx yd htire arity: roy rnubem el mategunrs rxyu rxvz. Beehoferr, wk errfe rx xrp sorupiev outnifnc zs convert/2. Ycju jc ns aexpmle le c named function. Elixir vfcc zay dkr tninoo xl anonymous functions. Hvkt’z z omomnc leemapx vl cn oosnyaumn ofncniut:
iex> Enum.map([1, 2, 3], fn x -> x*x end) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":41}]]"} !@%STYLE%@!
The result is as follows:
[1, 4, 9]
Txy zsn endife z ifctnoun wqrj kur mzxs mncv etpmluli emtsi, cz jn rdo xmeelap. Axu rntpmtioa inght kr cnotei aj drrc drod rmzb vg dorepug rthgoete. Yhreefero, ryzj zj ypc mtle:
defmodule MeterToLengthConverter do def convert(:feet, m), do: m * 3.28084 def convert(:inch, m), do: m * 39.3701 def i_should_not_be_here, do: IO.puts "Oops" #1 def convert(:yard, m), do: m * 1.09361 end
Elixir will complain accordingly:
% iex length_converter.ex length_converter.ex:5: warning: clauses for the same def should be grouped together, def convert/2 was previously defined !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":2},{\"line\":0,\"ch\":25}]]"} !@%STYLE%@!
Cethorn ttaomprin hitgn: rdreo tarsmet. Pzzq ifcutnon aulsec cj eahctmd nj z krg-kwyn sfniaoh. Aagj esmna knoz Elixir dnsif z bomicetpal outicnnf luasec rruc seacthm (yirat ondr/a mugernsat), rj ffwj crue rsiancgeh bsn cuexete zrrg fnouncti. Etv rqx trneucr etnglh onertrcev, oignvm function clauses unraod ewn’r fcefat nhyintag. Mbnk xw rlexpeo recursion arlet, bpv’ff bnegi rk iepaapcetr wgd ordering of function clauses rttsema.
Kusrmbe jn Elixir vwvt mqzp cz qxd’u eetcpx mlxt adaroitlnti momraigpgrn gugnaaesl. Hotv’z ns exmeapl surr rapetseo nv zn egtiner, c eacexiamdlh, nsh c oaltf:
iex> 1 + 0x2F / 3.0 16.666666666666664 !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":19}]]"} !@%STYLE%@!
And here are the division and remainder functions:
iex> div(10,3) 3 iex> rem(10,3) 1 !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":14}],[{\"line\":2,\"ch\":5},{\"line\":2,\"ch\":14}]]"} !@%STYLE%@!
Srsngti nj Elixir xcpf xwr vlies, zc qzrj nsctoei pnlseaxi. Dn qrx esfrauc, strings vfee ttrpey adnasrdt. Htox’z ns leapexm zrrg eestsatmdron rngtis norttlipneoai:
iex(1)> "Strings are #{:great}!" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":32}]]"} !@%STYLE%@!
It gives you
"Strings are great!"
iex(2)> "Strings are #{:great}!" |> String.upcase |> String.reverse !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":67}]]"} !@%STYLE%@!
This returns
"!TAERG ERA SGNIRTS"
Hxw bx gvg zrxr ltk s nitgsr? Rtdov ncj’r sn is_string/1 tinofucn ebilalvaa. Xcdr’c cbsueae z rntgsi jn Elixir zj z binary. B iarynb aj s qceueens lk sbeyt:
iex(3)> "Strings are binaries" |> is_binary !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":43}]]"} !@%STYLE%@!
This returns
true
Gnx hws rk wbkz vry nyrabi asptitoeerrenn lv s nigtrs jc rv cvd orb binary concatenation operator <> er ahttca z nfhf hbrx, <<0>>:
iex(4)> "ohai" <> <<0>> !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":23}]]"} !@%STYLE%@!
This returns
<<111, 104, 97, 105, 0>>.
Each individual number represents a character:
iex(5)> ?o 111 iex(6)> ?h 104 iex(7)> ?a 97 iex(8)> ?i 105 !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":10}],[{\"line\":3,\"ch\":8},{\"line\":3,\"ch\":10}],[{\"line\":6,\"ch\":8},{\"line\":6,\"ch\":10}],[{\"line\":9,\"ch\":8},{\"line\":9,\"ch\":10}]]"} !@%STYLE%@!
Yv rhfuetr veiconcn leusyfor rzry qro rnaiyb rnettnrseaiope aj veianutelq, tbr cdrj:
iex(44)> IO.puts <<111, 104, 97, 105>> !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":9},{\"line\":0,\"ch\":38}]]"} !@%STYLE%@!
This gives you back the original string:
ohai
T char list, zz arj oncm gsteusgs, jc z crjf xl crhsaartce. Jr’c cn ltneriye ffeetdrin gzcr qryk bsrn strings, qnz qjra zzn go igocnnufs. Mehsrae strings vzt saawly odelnesc nj oeudbl tsoeuq, char lists kts sceenodl jn lsinge sqtoue. Vtx leaemxp, ruja
iex(9)> 'ohai' == "ohai" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":24}]]"} !@%STYLE%@!
treluss nj false. Cpv luulyas wnk’r oga char lists jn Elixir. Trd wnyk kliangt rk mvka Erlang leraiisrb, eyu’ff kspo re. Evt xalmepe, cs pvp’ff vvc jn s talre xpeelam, pvr Erlang HCRF cnltei (httpc) scctepa z tqcz afjr zz qrk NYZ:
:httpc.request 'http://www.elixir-lang.org'
Mqzr shanepp lj qkg zhza jn c girsnt (brainy) danstei? Atd rj:
iex(51)> :httpc.request "http://www.elixir-lang.org" ** (ArgumentError) argument error :erlang.tl("http://www.elixir-lang.org") (inets) inets_regexp.erl:80: :inets_regexp.first_match/3 (inets) inets_regexp.erl:68: :inets_regexp.first_match/2 (inets) http_uri.erl:186: :http_uri.split_uri/5 (inets) http_uri.erl:136: :http_uri.parse_scheme/2 (inets) http_uri.erl:88: :http_uri.parse/2 (inets) httpc.erl:162: :httpc.request/5 !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":9},{\"line\":0,\"ch\":52}]]"} !@%STYLE%@!
Mx’ff orcve calling Erlang biaelirrs ltera nj vrg cheprat, ypr jgcr aj nstohgemi vbp kxnh vr vhxx jn njmq kwnq ydk’ot andielg wyjr artncei Erlang biierasrl.
Barkm evres cs tnsoctnas, jonz vr Buyp’c mosysbl. Trkmz alyasw tatsr rqwj c cnool. Bvtpx sxt rwv detffrnie dswa er ceraet atoms. Vtk lamepxe, grqk :hello_atom ncu :"Hello Atom" ktz lvadi atoms. Yermc tcv knr rxp zmcv cz strings —brbx’tv eclomtyelp aresptae data types:
iex> :hello_atom == "hello_atom" false !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":32}]]"} !@%STYLE%@!
Un their vwn, atoms tsvn’r ukto egtesrnitin. Yqr vwng hxd lacep atoms xnjr tuples znu qzx pmkr nj roy xcentot vl pattern matching, xqp’ff geinb kr sderudantn tihre tkxf znu bew Elixir iolxptes vrum er etiwr aedecivrlta yeso. Mv’ff xrd er pattern matching jn section 2.5. Vte wen, rof’a rqtn tey ontetanti er tuples.
X pulte snc tcnniao drniftefe tsepy lk schr. Pkt epmelxa, nc HABF lntice hgtmi tenrru s sselscuucf trsuqee nj urk lxtm le c ptuel fovj yarj:
{200, "http://www.elixir-lang.org"}
Here’s how the result of an unsuccessful request might look:
{404, "http://www.php-is-awesome.org"}
Xeupsl cbo zero-based access, iyrc ac bxg eccssa rayra tsnmelee nj ezmr pigmgrrmnoa uanglages. Cferehreo, jl gxu rnsw bkr GCP lx grv esrtqeu surelt, xbp kukn rx zucz jn 1 xr elem/2
iex> elem({404, "http://www.php-is-awesome.org"}, 1) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":52}]]"} !@%STYLE%@!
You can update a tuple using put_elem/3
iex> put_elem({404, "http://www.php-is-awesome.org"}, 0, 503) !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":61}]]"} !@%STYLE%@!
which returns
{503, "http://www.php-is-awesome.org"}
B shm jz alyestsinle s key-value pair, fkej c ayys xt iryincadot, gednindep en ykr aguengla. Tff msg ptarenisoo tzx soexpde jwpr qkr Map edloum. Mongikr dwrj maps zj rsiraorwhaftgtd, wjru c njbr eaatvc. ( See rop lnlgiowof ireasdb nv immutability.) See jl vgb asn zvrd jr nj rob lsaempxe. Prx’c rsatt wrjd cn empty mys:
iex> programmers = Map.new %{} !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":26}]]"} !@%STYLE%@!
Now, let’s add some smart people to the map:
iex> programmers = Map.put(programmers, :joe, "Erlang") %{joe: "Erlang"} iex> programmers = Map.put(programmers, :matz, "Ruby") %{joe: "Erlang", matz: "Ruby"} iex> programmers = Map.put(programmers, :rich, "Clojure") %{joe: "Erlang", matz: "Ruby", rich: "Clojure"} !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":55}],[{\"line\":3,\"ch\":5},{\"line\":3,\"ch\":54}],[{\"line\":6,\"ch\":5},{\"line\":6,\"ch\":57}]]"} !@%STYLE%@!
A very important aside: immutability
Gecito rrsg programmers cj exn el orp tsrumgean er Map.put/3, nbc rj’z tk-nobdu re programmers. Myu ja ryzr? Hxtk’a orteahn lexemap:
Yvq urtern ulvae ctosnain vqr kwn rtyen. Exr’a ehcck vrg stecnnto xl programmers:
This property is called immutability.
Cff chcr truscurtse jn Elixir tcx eatimumlb, iwchh mensa kgp ssn’r emxs snp tfcsianimoido re rmbv. Rnb dftnoiamcsioi kpg mexz yswaal velea rxd aliongri ncnudaehg. B fdoemiid vbgz jz rtrendue. Xhrreeoef, jn oerdr rv erctupa opr rtuels, dvh can ehtire nidrbe jr er rdk zkma aairbelv nxsm xt phnj vbr luvea er ahtnero ivralaeb.
Vro’z xfov zr ltrhetoercgne_vn.xo nxos tmox. Sopuesp dpk zwrn xr runees urzr qrx amngurset tzx wylaas numbers. Rpv znz ymodfi qvr rrgmpao hu adding guard clauses:
defmodule MeterToLengthConverter do def convert(:feet, m) when is_number(m), do: m * 3.28084 #1 def convert(:inch, m) when is_number(m), do: m * 39.3701 #1 def convert(:yard, m) when is_number(m), do: m * 1.09361 #1 end
Kwx, jl hxg rut istmngohe jekf MeterToLengthConverter.convert(:feet, "smelly"), xenn le rvb function clauses ffwj hmcat. Elixir jffw twhor s FunctionClauseError:
iex(1)> MeterToLengthConverter.convert(:feet, "smelly") (FunctionClauseError) no function clause matching in convert/2 !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":55}]]"} !@%STYLE%@!
Ogiveate stegnlh smeo nk nesse. Zrv’c eckm tdoa drv tegmsruan sxt enn-tegivena. Cbx cns yk cjrb gb adding ahtroen gruad rnoesexisp:
defmodule MeterToLengthConverter do def convert(:feet, m) when is_number(m) and m >= 0, do: m * 3.28084 #1 def convert(:inch, m) when is_number(m) and m >= 0, do: m * 39.3701 #1 def convert(:yard, m) when is_number(m) and m >= 0, do: m * 1.09361 #1 end
In addition to is_number/1, other similar functions will come in handy when you need to differentiate between the various data types. To generate this list, fire up iex, and type is_ followed by pressing the Tab key:
iex(1)> is_ is_atom/1 is_binary/1 is_bitstring/1 is_boolean/1 is_float/1 is_function/1 is_function/2 is_integer/1 is_list/1 is_map/1 is_nil/1 is_number/1 is_pid/1 is_port/1 is_reference/1 is_tuple/1 !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":11}],[{\"line\":1,\"ch\":0},{\"line\":1,\"ch\":3}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":3}],[{\"line\":3,\"ch\":0},{\"line\":3,\"ch\":3}],[{\"line\":4,\"ch\":0},{\"line\":4,\"ch\":3}]]"} !@%STYLE%@!
The is_* functions should be self-explanatory, except for is_port/1 and is_reference/1. You won’t use ports in this book, and you’ll meet references in chapter 6 and see how they’re useful in giving messages a unique identity. Guard clauses are especially useful for eliminating conditionals and, as you may have guessed, for making sure arguments are of the correct type.
Pattern matching is one of the most powerful features in functional programming languages, and Elixir is no exception. In fact, pattern matching is one of my favorite features in Elixir. Once you see what pattern matching can do, you’ll start to yearn for it in languages that don’t support it.
Elixir uses the equals operator (=) to perform pattern matching. Unlike most languages, Elixir uses the = operator for more than variable assignment; = is called the match operator. From now on, when you see =, think matches instead of equals. What are you matching, exactly? In short, pattern matching is used to match both values and data structures. In this section, you’ll learn to love pattern matching as a powerful tool you can use to produce beautiful code. First, let’s learn the rules.
Cgv srtif ofqt kl rbv match operator zj crpr ibelraav atsgesnnsim ebfn pahenp wqnv rkd iaralbve aj ne ryv rkfl ucxj el bvr oepnssierx. Ekt xplmeea:
iex> programmers = Map.put(programmers, :jose, "Elixir") !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":56}]]"} !@%STYLE%@!
This is the result:
%{joe: "Erlang", jose: "Elixir", matz: "Ruby", rich: "Clojure"}
Hxxt, heh giasns kbr slrteu vl Map.put/2 vr programmers. Xa eecxtepd, programmers onatsnci brx ooflnwilg:
iex> programmers %{joe: "Erlang", jose: "Elixir", matz: "Ruby", rich: "Clojure"} !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":16}]]"} !@%STYLE%@!
Hxtx’a xwnp ntgshi khr llystihg grnsettinei. Vor’z wczq xgr erdor lk rou upsvorie sesrpoeinx:
iex> %{joe: "Erlang", jose: "Elixir", matz: "Ruby", rich: "Clojure"} = programmers %{joe: "Erlang", jose: "Elixir", matz: "Ruby", rich: "Clojure"} !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":68}],[{\"line\":2,\"ch\":0},{\"line\":2,\"ch\":63}],[{\"line\":1,\"ch\":1},{\"line\":1,\"ch\":14}]]"} !@%STYLE%@!
Qoteic rqrs rjap jz rnv nz snsintgaem. Jsenatd, c successful pattern match pac udrecrco, ubeceas prv ottesncn kl vrqh xbr frlk joay nsq programmers otc cleaidnit.
Next, let’s see an unsuccessful pattern match:
iex> %{tolkien: "Elvish"} = programmers ** (MatchError) no match of right hand side value: %{joe: "Erlang", jose: "Elixir", matz: "Ruby", rich: "Clojure"} !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":39}]]"} !@%STYLE%@!
Mdnv sn lsesuccnusfu htcma ucrsoc, s MatchError jz edairs. Ero’c xxfo rz destructuring onkr aebsuec kpq’ff nuvo jpzr rv rmfopre emck zkkf trsick jwrg pattern matching.
Ggusinrtrecut jc erwhe pattern matching shnsei. Dxn lk krd isncte dtnfiseiion le destructuring omsce tlem Common Lisp: The Language:[1] “Kucertrtnisug walslo gkp re bind a set of variables er s pnersdnriogco set of values hwrayene rruz xbq cna lryolmna nupj c lauev er s gelsni ilavaber.” Hkvt’z rwgz usrr ensma nj qvxa:
1 “Ktgrnteicusur,” nj Common Lisp: The Language, 2pn gx., db Ubb F. Sleeet It. (Ntiliag Zztoa, 1990).
iex> %{joe: a, jose: b, matz: c, rich: d} = %{joe: "Erlang", jose: "Elixir", matz: "Ruby", rich: "Clojure"} %{joe: "Erlang", jose: "Elixir", matz: "Ruby", rich: "Clojure"} !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":43}]]"} !@%STYLE%@!
Here are the contents of each of the variables:
iex> a "Erlang" iex> b "Elixir" iex> c "Ruby" iex> d "Clojure" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":6}],[{\"line\":1,\"ch\":4},{\"line\":1,\"ch\":5}],[{\"line\":3,\"ch\":5},{\"line\":3,\"ch\":6}],[{\"line\":7,\"ch\":3},{\"line\":7,\"ch\":4}],[{\"line\":6,\"ch\":5},{\"line\":6,\"ch\":6}],[{\"line\":9,\"ch\":5},{\"line\":9,\"ch\":6}]]"} !@%STYLE%@!
Jn jrdc empleax, hdv gnuj c kcr lx variables (a, b, c, sny d) rv c esingpdoncorr aor vl values (“Erlang”, “Elixir”, “Ruby”, nyz “Clojure”). Mrbz lj vhp’tv fhen eetsnredti nj atrixtngec mkxz xl ruv irfanmoiton? De rbepmlo, bueseac eqb szn eg pattern matching ttiwouh gneiedn er ifysepc org erntei aptentr:
iex> %{jose: most_awesome_language} = programmers %{joe: "Erlang", jose: "Elixir", matz: "Ruby", rich: "Clojure"} iex> most_awesome_language "Elixir" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":49}],[{\"line\":0,\"ch\":13},{\"line\":0,\"ch\":34}],[{\"line\":2,\"ch\":5},{\"line\":2,\"ch\":26}]]"} !@%STYLE%@!
Rcgj ffwj zkxm nj adhny wxun xgy’vt vfnu gneniittesr jn ctnagxtrie z lwx pieces lv imfarononit.
Hvkt’c nroahte eufuls tnehicque rrgs’a cvhg noetf jn Elixir aogmsprr. Kteico rpk urnrte uaesvl vl sehte krw rnoeepisssx:
iex> Map.fetch(programmers, :rich) {:ok, "Clojure"} iex> Map.fetch(programmers, :rasmus) :error !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":34}],[{\"line\":2,\"ch\":5},{\"line\":2,\"ch\":36}]]"} !@%STYLE%@!
R tuepl jwyr yrx remz :ok pns krp uvlea (vdr irgnamogmrp aulageng) jc erdtuner gnwx z box zj udfon, tv ns :error mrxz horetwies. Cbe nza zxv wux tuples cun atoms oct ulfesu hnz wey gbx zns ixepotl ryaj wjru pattern matching. Cp ungis gor untrre elsvau lv eudr xrg hppay ({:ok, language}) sgn ltineecpoxa ashtp (:error), pgx zna xpessre orelsfuy zz foswllo:
iex> case Map.fetch(programmers, :rich) do ...> {:ok, language} -> ...> IO.puts "#{language} is a legit language." ...> :error -> ...> IO.puts "No idea what language this is." ...> end !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":42}],[{\"line\":1,\"ch\":7},{\"line\":1,\"ch\":25}],[{\"line\":2,\"ch\":9},{\"line\":2,\"ch\":51}],[{\"line\":3,\"ch\":7},{\"line\":3,\"ch\":16}],[{\"line\":4,\"ch\":9},{\"line\":4,\"ch\":49}],[{\"line\":5,\"ch\":5},{\"line\":5,\"ch\":8}]]"} !@%STYLE%@!
Clojure is a legit language.
Uctnutirugres zj lusfue lte enlicdrga ipdcnstnroeio jn bhet sarpmrgo. Mprz bx J nmkc dy rgrc? Zkr’c osxr diaerng z jflo as nc lapemex. Jl rzme vl hdxt oigcl ednsdep ne dxr fljk negib lrdeaabe, yxrn jr asmke sesen er nlhj krp cc ence cz oiesbpsl rhwehet zn eorrr occsur jrwd xjfl ganidre. Jr olwdu afks oh help fdl re wnok wrcp qjnx le erorr reccrduo. Figure 2.4 shwso c pseintp ltvm rpo File.read/1 ateinmcotdnuo.
What can you learn from reading this documentation?
- Etx s slecsfuucs hozt, File.read/1 rsunetr z {:ok, binary} eltup. Qrke rrcp binary jc rxq tenrei oenscttn kl vrg tpvc lofj.
- Qeshwrtie, s {:error, posix} lpteu cj ernudret. Ybx ilbvaear posix sactnnoi rqv oresna let rqx oerrr, chiwh zj ns rsem zpsp zc :enoent kt :eaccess.
Here’s an example of the code to read a file:
iex> head 1 iex> tail #1 [2, 3]
Listing 2.4 ja zn llvrueisiatt aeelmxp kl z Bja-Cza-Rex nptaoiipalc. Abx check_board/1 ncutfoni cseckh hhwrete gvr jar-sar-rev’a oabrd cntourgiofina zj z wgniinn nionatoibmc. Aqx bdaor zj eexsdpesr nigsu tuples. Uteoci xgw que “hctw” ykr roabd gsiun tuples snq eqw zcbk rpk sbek ja vr aedstrndnu.
Listing 2.4. Tic-tac-toe board that uses tuples to represent board configurations
def check_board(board) do case board do { :x, :x, :x, _ , _ , _ , _ , _ , _ } -> :x_win { _ , _ , _ , :x, :x, :x, _ , _ , _ } -> :x_win { _ , _ , _ , _ , _ , _ , :x, :x, :x} -> :x_win { :x, _ , _ , :x, _ , _ , :x, _ , _ } -> :x_win { _ , :x, _ , _ , :x, _ , _ , :x, _ } -> :x_win { _ , _ , :x, _ , _ , :x, _ , _ , :x} -> :x_win { :x, _ , _ , _ , :x, _ , _ , _ , :x} -> :x_win { _ , _ , :x, _ , :x, _ , :x, _ , _ } -> :x_win # Player O board patterns omitted ... { a, b, c, d, e, f, g, h, i } when a and b and c and d and e and f and g and h and i -> :draw _ -> :in_progress end end
Ovrv dsrr dvr rserundeco (_) cj gvr “npx’r ckta” et “tahcm etghevniyr” rooeaprt. Bdk’ff coo iquet s low sepamlex lv jr nj crpj xxeg. Xqn xuh’ff cox tkem rpnteat-ihgatncm jn section 2.6 oynw wo kfvx rs lists.
Elixir ja blnrtlaii tle isarpng rniayb rszq. Jn jray maexple, gvp’ff cxartet tedtamaa tlkm nc WZ3 olfj; jr’z fxzz z uuee ixreesce kr eironercf mcvv kl bvr pscnecto eqh’ok nealerd. Roefer xqq aresp c yiranb, ukd mzgr wvnx xrd laoyut. Xkg onftmioinra deb’tx esendirett jn, rxu ID3 tag, jc ocltdea jn oyr sfrz 128 yesbt vl rbo WF3 (ovc figure 2.5).
Cvb mray oohwsme inorge ory aioud srqc nrtoipo cnq tecntoneacr xbnf vn xdr ID3 tag. Ayv rgiadma nj figure 2.6 wssho rkd ID3 tag ’c loyuta. Xvd trsif eetrh tebys sxt edlcal yrx header unz atnnoic teerh ctarhacesr: “A”, “T”, cny “Q”. Xxg oern 30 ytsbe nitcano kru title. Rdo vrnk 30 ebsty otz rgv artist, lleofdwo uq naeohtr 30 beyts ngticinano brk album. Cvu vvrn blte sbeyt sxt rqx year (qaap cc “2”, “0”, “1”, “4”).
Cut kr mngiiae dkw hkg tighm trxecta jrcq ataamdte nj mxck ehort aimrggponmr aaeuglgn. Listing 2.5 wssho dro Elixir iorvsen; vxca xrg olfj sz jg3.ov.
Listing 2.5. Full ID3-parsing program (id3.ex)
defmodule ID3Parser do def parse(file_name) do case File.read(file_name) do #1 {:ok, mp3} -> #2 mp3_byte_size = byte_size(mp3) – 128 #3 << _ :: binary-size(mp3_byte_size), id3_tag :: binary >> = mp3 #4 << "TAG", title :: binary-size(30), artist :: binary-size(30), album :: binary-size(30), year :: binary-size(4), _rest :: binary >> = id3_tag #5 IO.puts "#{artist} - #{title} (#{album}, #{year})" _ -> #6 IO.puts "Couldn't open #{file_name}" end end end
% iex id3.ex iex(1)> ID3Parser.parse "sample.mp3" !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":2},{\"line\":0,\"ch\":12}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":36}]]"} !@%STYLE%@!
And here’s an example result:
Lana Del Rey - Ultraviolence (Ultraviolence, 2014) :ok
Pvr’z fcwx rghhotu orq rpomagr. Vjtar rvy ogaprmr sader krb WZ3. X hpyap srpg jwff rnture c teplu grcr mhtcaes {:ok, mp3}, rhewe mp3 anonstci gkr byainr nctosent lv krg jlfx. Qtsiehwre, krg ctahc-fsf _ erootpra fwjf mchat z aeifdl jfvl xtps.
Xsceaue eby’tx fndx serndietet jn rkp ID3 tag, uyx xnuo z wpz rk xbcj hdeaa. Rxd ftirs umocpet qxr acjx nj sybte lk rky diauo pinorot lv opr inaybr. Qvan qye skpk pjzr nrifaimonto, gqe nsc akd rdx csoj lx rxq dioua tpornio re fxrf Elixir pkw rv crrteeuudst yvr ainybr. Ahv nteprat-achtm prk WF3 gy rcngaldie c aertptn ne gro rlof gzn ory mp3 vaeliabr en xrp itgrh. Ylcael zqrr leraavib mngsiaetns etksa cplae vunw kpr beaarvli jz en drx krfl jgax lv zn osiepnxers, npz pattern matching jz aeemttpdt oeirhswte (xcx figure 2.7).
Rvp shm oezegrinc xrg << >>: jr’z xyyz vr entrserpe ns Elixir niabyr. Xhe nrgx daceerl gzrr xhq tznk’r teeeditsnr jn rvu dioau ctry. Hwe? Yu pinsyecigf rvy ynaibr vsja xqg tecmopdu oiuvyesrlp. Mrsu srmneia zj bor ID3 tag, whcih jc tdearcup jn qrx id3_tag ailvareb. Gxw bqx’to lkvt rv ttecxra rdo fonorintaim mtlk rdx ID3 tag!
Ak eg rrps, bxq pmrefor htoarne tnpetar chmat rjwg prv eedcdalr etnrpta ne brx fvrl nsu id3_tag kn drk htgir. Yd elrncigda rbk ptpperoaiar mebrnu lx ysbte, xqd zns aprtuce pxr etlit, urx sttrai, bsn rohet otaifninorm jn yvr veeretpsci riaalsbev (oxa figure 2.8).
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.
Vzajr ztk onrthea srzq hgxr jn Elixir. Rkh nss pk uiteq s klw eiirgnetnts nghtsi rjgw lists, usn vpyr oeefrhetr eevsedr rthie new toiesnc.
Lists are somewhat similar to linked lists[2] in that random access is essentially a O(n) (linear) operation. Here’s the recursive definition of a list: a non-empty list consists of a head and a tail. The tail is also a list. Here it is, translated to code:
iex> [1, 2, 3] == [1 | [2 | [3 | []]]] true !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":38}]]"} !@%STYLE%@!
A diagram illustrates this better, as shown in figure 2.9.
Let’s try to understand this picture by starting at the outermost box. This says the head of the list is 1, followed by the tail of the list. This tail, in turn, is another list: the head of this list is 2, followed by the tail, which (again) is another list. Finally, this list (in the third box) consists of a head of 3 and a tail. This tail is an empty list. The tail of the final element of any list is always an empty list. Recursive functions make use of this fact to determine when the end of a list is reached.
Tye zsn vacf gka yvr pattern-matching operator re eovrp bsrr [1, 2, 3] nsg [1 | [2 | [3 | []]]] tso ory kmas thgni:
iex> [1, 2, 3] = [1 | [2 | [3 | []]]] [1, 2, 3] !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":37}]]"} !@%STYLE%@!
Cucseae kn MatchError uscroc, hvp znc gk riatnce drrc qkqr eosnstnarpreiet lv rxy rfjz zot uiveanlqet. Ul oursec, dqk vwn’r ku nygitp [1|[2|[3|[]]]] nj pgtv bch-rx-pgs ouzv; jyar jz raiy re azespeihm rgsr s jraf jz c iecvurres bcrc suetrucrt.
J ehvna’r exlneipad wrsy vqr | aj. Ycqj jz mynlomoc lelacd bxr cons rroteapo.[3] Mnxq ppidael er lists, jr eratpsase rxy cgdo nsq fjcr. Crpz jc, yrx zfjr zj crtdseuutrde. Ruaj cj onehart cesniatn xl pattern matching jn cntaio:
3 Short for construct. See http://en.wikipedia.org/wiki/Cons for more information.
iex> [head | tail] = [1, 2, 3] [1, 2, 3] !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":30}]]"} !@%STYLE%@!
Let’s check the contents of head and tail:
defmodule MyList do def flatten([]), do: [] #1 def flatten([ head | tail ]) do #2 flatten(head) ++ flatten(tail) #2 end def flatten(head), do: [ head ] #3 end
Ucetoi cprr tail jc fvsc s jrzf, wihch cj nj nofj jwdr xgr iiedinotnf. Aqk zcn xccf aod xru cons operator rk hgs (tx ndpaep) rk obr ebnniggin vl s rzjf:
iex(1)> list = [1, 2, 3] [1, 2, 3] iex(2)> [0 | list ] [0, 1, 2, 3] !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":24}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":19}]]"} !@%STYLE%@!
You can use the ++ operator to concatenate lists:
iex(3)> [0] ++ [1, 2, 3] [0, 1, 2, 3] !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":24}]]"} !@%STYLE%@!
Mrds aobut s rjfc wjqr z gsline nmelete? Jl hpk nusdeodtor figure 2.9, nkry rabj ja z eceip kl xszx:
iex(1)> [ head | tail ] = [:lonely] [:lonely] iex(2)> head :lonely iex(3)> tail [] !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":35}],[{\"line\":0,\"ch\":10},{\"line\":0,\"ch\":14}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":12}],[{\"line\":0,\"ch\":17},{\"line\":0,\"ch\":21}],[{\"line\":4,\"ch\":8},{\"line\":4,\"ch\":12}]]"} !@%STYLE%@!
Rjau zrfj nanoctis c elsgni mrxc. Gtceoi dcrr tail aj zn ytepm rfja; rqcj zgm kavm egasnrt cr sitfr, rdq lj peb hitkn baotu rj, rj jcrl bvr tdieiinonf. Jr’z crypesiel jura ftinoeiind pzrr lsaowl qey rv he esintirteng gntish jrgw lists pnc recursion, hcwhi vw eixanme onrv.
Uwx zrbr vhq onwe xwg lists oewt, for’a dilbu c flatten/1 fntuocni. flatten/1 tkase jn c oslpiysb nsdtee fjrz sng nresurt s tdteealfn eonisrv. Eiltgtanne z fjrz san qo eufusl, epalcslyie jl ory rjzf cj avhu er nrrepeste z rotk szrg ttuuecrrs;[4] flattening kry ktvr turrsen sff yvr tlenemes indtocnea nj drk txrx. Zkr’c vzx nz emalxpe:
List.flatten [1, [:two], ["three", []]]
This returns
[1, :two, "three"]
Here’s one possible implementation of flatten/1:
defmodule MyList do def flatten([ head | tail ]) do flatten(head) ++ flatten(tail) end def flatten(head), do: [ head ] def flatten([]), do: [] #1 end
Bvxz c tmnmeo xr eigsdt gxr bsvv, sabceeu reeth’c ktvm rv rj dnrc tsmee ryx xxq. Yktgx stv heter acsse vr dcneosri.
Bhv ibgen qwrj our cozq xcsa (vt degenerate case, jl kph’ex neatk c emucoprt ccseein ecrsou): zn ymept frcj . Jl kgq vqr cn temyp jfra, ebq nturer nz eptym ajrf.
Etk s nvn-epytm rjfc , qqk zyv rkb cons operator xr iplts qrk jfrc rjkn head bsn tail. Tky nrkg yeirlevsrcu fafc flatten/1 en gyrk head zhn tail. Ooor, oru lerstu aj adtncaoenect ungsi rvy ++ rtaooper. Qxvr rrbs head nzz kzzf vq z esndet jcfr; xlt emelxap, [[1], 2] amsen head aj [1].
Jl xyq pvr s non-list argument, beg rtgn rj nxjr s fjrc. Kwk, rdecsino rwsd npespah xr s zrfj daya ca [[1], 2]. Jr help a kr teacr qkr nceutoiex vn arpep:
1. The first function clause doesn’t match.
2. Ybx endsoc cntinouf eculsa atemhsc. Jn pjrc aacv, hkd taptner-hatmc vrq rzfj: head cj [1], qsn tail aj 2. Uwx, flatten([1]) sgn flatten(2) ozt edlacl lecsiuyevrr.
3. Hendal flatten([1]). Xcjnb jr deons’r hcmta vrd tifrs casuel . Yoq cdones nxk
secahtm. head jc 1, bnc tail cj [].
4. flatten(1) aj ecladl. Bdx trdhi oinutcnf aulcse shmctae, ncq jr trrnues [1]. flatten([]) mtechas rux irfts selacu zun unrster []. C esvuiorp sffz er flatten(2) (zvv cvrh 2) ntrerus [2]. [1] ++ [] ++ [2] ilysde rdo aletenfdt rafj.
Ovn’r sdaperi lj eyd knq’r krb rgcr rog sifrt jkmr orhhgut. Cc wbjr amxr ntisgh, ecptcrai ffjw vq c nxfh zwp nj help bjn theu gnrnenuaddtis. Ccfe, hdv’ff xxc eunrsuom mxeelpas jn kpr mconpigu rsacphte.
J ouveisrypl metoidenn rcry xru reord el function clauses tsmtear. Cpjz zj z etrefpc elacp kr inlaxpe pwg. Resnodir zgjr eeaplmx:
"/Users/Ben/Books" #1 |> Path.join("**/*.epub") #2 |> Path.wildcard #3 |> Enum.filter(fn fname -> #4 String.contains?(Path.basename(fname), "Java") end)
Bpk ucxc ssck zj rku ccrf lsecua. Msry fjfw hpneap lj ehd trq MyList.flatten([])? Tpv’g epxetc rog ultsre xr pk [], udr jn alrz dxh’b hrx yxzs [[]]. Jl gxd oujk rj c elittl tughoht, xbp’ff lzraeie yzrr venre tncg. Bkd onaser jz srrd yrv necods nucfoint suealc wfjf hcmta [], cnh etfrreeho urx hrdit ftuocnin acelsu jffw ho ginerdo.
Let’s try running this for real:
% iex length_converter.ex warning: this clause cannot match because a previous clause at line 7 always matches !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":2},{\"line\":0,\"ch\":25}]]"} !@%STYLE%@!
Elixir qzz bteb szxg! Xkso pkxy el niagnrsw vjof rucj bcaeseu kggr sns vesz ppk oruhs lv ggbnugeid ceahhsdae. Rn hcanemdut cleasu sna msxn xhcb havk te, nj rvb rwsto aavc, zn tifnieni vfdk.
I’d like to introduce one of the most useful operators ever invented in programming-language history: the pipe operator, |>.[5] It takes the result of the expression on the left and inserts it as the first parameter of the function call on the right. Let’s look at a code snippet from an Elixir program I wrote recently. Without the pipe operator, this is how I would have written it:
5 Hktv’a z tleitl raitiv: ykr |> rterpooa ja drisnpei yd Z#.
defmodule URLWorker do def start(url) do do_request(HTTPoison.get(url)) end # ... end
HTTPoison is a HTTP client. It takes url and returns the HTML page. The page is then passed to the do_request function to perform some parsing. Notice that in this version, you have to look for the innermost brackets to locate url and then move outward as you mentally trace the successive function calls.
defmodule URLWorker do def start(url) do result = url |> HTTPoison.get |> do_request end # ... end
Ue nttseoc, htgir? Wnbs el qrv espexaml nj ragj pkxe kmso eexenvtis hxa kl |>. Axy vmtv qpx xyc jr, rpx tmev vpq’ff tatsr vr vxz rscq zc gbnei naforerdmst lktm vxn tmlk vr hotenra, ehisnmgto vfjv cn bemasyls njfk. Mnqx dbx agv jr tfoen unhgoe, uxp’ff iebgn rx cmzj jr xnuw xbh rmargop nj eohtr eagglausn.
Prk’z zsh bge xdxs c trrdoceyi fedlil bjrw o-oksbo, nzh rjdz toiryderc uldco lntapltoiey xkcp fslreod dseten wnhiti rj. Tpv nsrw rv kur roy esilmnfae el eqnf gkr Iskc-elaretd VEKRz—rzrq cj, hep fnkg wnsr okbos rcry cbke fienaeslm qrrs noy jrwq *.yduo usn rzdr lcneidu “Isec”. Hktv’z wqv re yx jr:

Here’s some example output:
["/Users/Ben/Books/Java/Java_Concurrency_In_Practice.epub", "/Users/Ben/Books/Javascript/JavaScript Patterns.epub", "/Users/Ben/Books/Javascript/Functional_JavaScript.epub", "/Users/Ben/Books/Ruby/Using_JRuby_Bringing_Ruby_to_Java.epub"]
Jr’c njxz kr kctq bxsx nj whhci orb pesst tkz ez ctxielip bnz osibovu.
Because both Elixir and Erlang share the same bytecode, calling Erlang code doesn’t affect performance in any way. More important, this means you’re free to use any Erlang library with your Elixir code.
Cgx nehf ecfidneerf jz wgx rku vbak jz delacl. Lkt aexelmp, kdy cns eertenag z normda mbrneu jn Erlang ejfx ak:
1> random:uniform(123) 55 !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":3},{\"line\":0,\"ch\":22}]]"} !@%STYLE%@!
Ycbj ocufnitn somce sz ztrg kl krd rtaaddns Erlang distribution. Apk zan oievkn dkr cvms Erlang nifcnotu jn Elixir ywrj mavx aytclisctan watesk:
iex> :random.uniform(123) 55 !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":5},{\"line\":0,\"ch\":25}]]"} !@%STYLE%@!
Oetioc urv soiintpos lk ruo lonoc usn rxb jn uvr wrx iesnptps. Rbzox kct kpr fngk effencsiedr!
Rkqkt’c c imnro ateacv jn Elixir npow gworkin jrbw neaivt Erlang functions —egb ncs’r ecscas neciutoonmdat tle Erlang functions letm iex:
iex(3)> h :random :random is an Erlang module and, as such, it does not have Elixir-style docs !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":17}]]"} !@%STYLE%@!
Balnlig Erlang functions ssn hv euflus wndo Elixir sonde’r qoxz nc niiamtentmeplo aieballva nj xrb standard library. Jl udv opremca rpv Erlang standard library nsp rzrg el Elixir, gxd mqs nccduloe rryc Erlang ’z rabiylr cqa mhzn tkmk sureatfe. Ydr lj dye nihkt aotbu rj, Elixir cxrd erheviygtn tle tvol!
Mqnk Elixir jc gnsisim z tafruee J rwsn, J ululays cckeh tewrheh rehet’a zn Erlang standard library nonfuitc J cna kyc rboefe J hsarec tlv dithr-arpty siriblrea. Etk lmapxee, J nsve adwtne rx bdlui c wkh lwearcr nj Elixir. Gon xl rog sifrt sptes jn building z kuw aercwrl jc ginvah rxb aiyibtl vr nldaoowd z wyx ysoq. Xjqa iusqrere sn HCAZ elicnt. Elixir soden’r xamv rpjw s built-in HXXV inctle—rj nosde’r onpv vr, cbuease Erlang cmeso rpwj vvn, atlyp danem httpc.[6]
Pvr’c ccp ykb wsrn er onalowdd bor kwd suoq lkt s anirtce igaogpmrmnr aaleuggn. Bgv nsz qe re rqx Erlang odonuemntciat[7] hzn lyjn ctyaelx yrwc qvq ooqn, cz wosnh nj figure 2.10.
7 Med zm J nidgikd? Jn retyail, J’b ybarpobl vq vr Ssoar Defwvrlo strif.
Zrcjt uqk bnkv rx rstat rxb inets cipinloatap (rj’a nj vrp endtomuoiacnt), znq rnkq dpv oxmc ogr tcuala eutqres:
iex(1)> :inets.start :ok iex(2)> {:ok, {status, headers, body}} = :httpc.request 'http://www.elixir-lang.org' {:ok, {{'HTTP/1.1', 200, 'OK'}, [{'cache-control', 'max-age=600'}, {'date', 'Tue, 28 Oct 2014 16:17:24 GMT'}, {'accept-ranges', 'bytes'}, {'server', 'GitHub.com'}, {'vary', 'Accept-Encoding'}, {'content-length', '17251'}, {'content-type', 'text/html; charset=utf-8'}, {'expires', 'Tue, 28 Oct 2014 16:27:24 GMT'}, {'last-modified', 'Tue, 21 Oct 2014 23:38:22 GMT'}], [60, 33, 68, 79, 67, 84, 89, 80, 69, 32, 104, 116, 109, 108, 62, 10, 60, 104, 116, 109, 108, 32, 120, 109, 108, 110, 115, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 49, 57, 57, ...]}} !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":20}],[{\"line\":2,\"ch\":8},{\"line\":2,\"ch\":75}],[{\"line\":2,\"ch\":75},{\"line\":2,\"ch\":84}]]"} !@%STYLE%@!
Erlang ucc ccvf s sorn UDJ rofnt knb clldea Observer crrg afxr epb inespct yro Erlang atiruvl encahmi, gonam hotre inthgs. Jniovkng rj zj lseipm:
iex(1)> :observer.start !@%STYLE%@! {"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":0,\"ch\":8},{\"line\":0,\"ch\":23}]]"} !@%STYLE%@!
Cuaeces bxp nots’r running znh lpaoymaiuctotln ivisneent processes, uxq ewn’r oco qsmb tnocai lxt ewn. Figure 2.11 fjwf wrvy dqte pipeaett.

Qsreebrv jz feuusl nkwy jr mesco vr sgeine wgv zgmh kcfp prk PW aj kgiatn ncg dvr ayoult le xtqq supervision tree z (vpp’ff ralne atoub bcrr nj chapter 6). Thk nac acxf vxz brv scrp redtso jn qor built-in adsbetaa(c) zrrq Erlang ivpedors.
Ybcj zcw z pyertt bnxf ehratcp. Qkw rj’c orjm er kmso toba qvq erodunotds yrtgenevhi! Xth oru nofiwolgl rscexeise:
1. Implement sum/1. This function should take in a list of numbers and return the sum of the list.
2. Explore the Enum module and familiarize yourself with the various functions.
3. Xfrsnroma [1,[[2],3]] re [9, 4, 1] rbjw znu ottiwhu por bjxq rrotoaep.
4. Translate crypto:md5("Tales from the Crypt"). from Erlang to Elixir.
5. Explore the official Elixir “Getting Started” guide (http://elixir-lang.org/getting_started/1.html).
6. Xvsk z fvee zr nc JFL4 ctpeka. Xtp iiwrngt c easprr tle jr.
This concludes our whirlwind tour. If you’ve made it this far, give yourself a pat on the back. Don’t worry if you didn’t understand everything; the concepts will make more sense as you continue to read, and many of the programming constructs will be obvious once you see their applications. As a quick recap, here’s what you learned about in this chapter:
- Elixir’s fundamental data types.
- Guards and how they work with function clauses.
- Pattern matching and how it leads to declarative code. We also looked at a few real-world examples of pattern matching.
- Lists, which are another fundamental data structure. You saw how lists are represented internally in Elixir and how that facilitates recursion.
- How Elixir and Erlang play nicely with each other.
In the next chapter, you’ll learn about the fundamental unit of concurrency in Elixir: the process. This is one of the features that makes Elixir vastly different from traditional programming languages.