Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
Erlang for Skeptics rev 31 Luke Venediger Links: Hom...
44 downloads
745 Views
301KB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
Erlang for Skeptics rev 31 Luke Venediger Links: Home, Latest Version, RSS Feed for Updates Last Updated: 06 Dec, 2009 Table of Contents Chapter 1: Introduction Chapter 2: Build a Handy Calculator Section 2.1: Before We Begin Section 2.2: Basic Arithmetic Operations Section 2.3: Storing Values in Variables Section 2.4: Functions: Reusable Pieces of Code Section 2.5: Using Modules to Group Functions Subsection 2.5.1: Creating the Calculator Module Subsection 2.5.2: Adding A Function to the Calculator Module Subsection 2.5.3: Add the Remaining Functions Subsection 2.5.4: What Happens When You Compile a Module Section 2.6: Checking For Bad User Input Section 2.7: Summing More Than Two Numbers Subsection 2.7.1: Introducing Lists Subsection 2.7.2: Using Recursion to Sum a List Section 2.8: What We Have Covered Chapter 3: Build a Grocery Store Section 3.1: Before We Begin Section 3.2: Providing Groceries to Buyers Subsection 3.2.1: A Few Rules about Atoms Section 3.3: Create the Shop Module Section 3.4: Handle Unknown Grocery Items Subsection 3.4.1: Wildcards and Underscore Variables Section 3.5: Catering for Zero-Item Purchases Subsection 3.5.1: Formatting Strings with io:format/2 Section 3.6: Handling Bulk Orders Subsection 3.6.1: The Shopping List Section 3.7: What We Have Covered Chapter 4: Single-Player Rock, Paper, Scissors Section 4.1: Before We Begin Section 4.2: Analysing The Game Section 4.3: The Game Module: rpsgame Subsection 4.3.1: Pattern Matching The Same Variable Twice Section 4.4: Randomising the Computer’s Attack Section 4.5: Tracking Errors with Stack Traces Section 4.6: Using the Debugger Subsection 4.6.1: Using the Debugger Toolbar Subsection 4.6.2: How to Set a Breakpoint Subsection 4.6.3: Changing Variables At Runtime Section 4.7: A Tour of Erlang’s Help System Subsection 4.7.1: Understanding Erlang’s Documentation. Subsection 4.7.2: Finding Help On a Module or Function Section 4.8: More about Modules Subsection 4.8.1: Modules are Self-Describing Subsection 4.8.2: Code to Extract Version Information Subsection 4.8.3: Versioning and Module Metadata Chapter 5: Write a PNG File Parser Section 5.1: Find All PNG Files Chapter 6: Multi-Player Rock, Paper, Scissors Chapter 7: Write a Web Service using Mochiweb Chapter 8: Hanging References! Section 8.1: Function Bodies Section 8.2: Nested Case Statements Section 8.3: Wildcard vs Underscore Variables Gotcha Chapter A: Erlang Data Structures Section A.1: Comparison Operators Chapter B: Erlang Editors and IDEs
1 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
1 Introduction The introduction.
2 Build a Handy Calculator Everyone loves a good calculator, and this is exactly what we’re going to build with Erlang. We’ll be spending most of our time in the Erlang shell, entering commands and seeing the output straight away [A→] [→A] This also known as a Read-EvaluatePrint-Loop (REPL) and is a great way to test an expression and see its output quickly. .
2.1 Before We Begin Have a place to store your work as you go through this chapter. Create a directory called “e4s/chapter2↑”: ~$ mkdir -p ~/e4s/chapter2↑ ~$ cd ~/e4s/chapter2↑ ~/e4s/chapter2↑$
Now start the Erlang shell: $ erl Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false] Eshell V5.7.2 (abort with ^G) 1>
The shell has started and the prompt
1>
is awaiting further instructions.
2.2 Basic Arithmetic Operations First we must explore the four basic math operations: Addition, Subtraction, Multiplication and Division. Adding two numbers is straightforward: 1> 50 + 60. 110
Now, if you’re wondering why there’s a period at the end of this expression, just know that this is how we tell Erlang to go ahead and evaluate this code. If we leave it off then the shell will wait patiently until we put one in. Like so: 2> 50 + 60 2> . 110
Note: An expression must end with a period. Lets do a few more operations: 3> 100 - 90. 10 4> 15 - 100. -85 5> 100 * 50. 5000
At this point you may be wondering what the limits of an integer are in Erlang. I’m happy to tell you that the maximum size for an integer or float is limited by the amount of RAM on your computer. This is a perfectly valid calculation: 6> 4294967295 * 4294967295. 18446744065119617025
The result is certainly greater than the maximum value for a signed 32-bit integer [B→] [→B] If you’re wondering how I came up with this number, try entering 16#FFFFFFFF. into your Erlang shell. You can choose what your numbers are based in. In this case I’m writing a base-16 number. The base part can range from 2-32. . How about some division: 6> 100 / 50. 2.0
Hmmm that last line looks a little out of place. Up to now we’ve been working with integers, and all our results have been whole numbers. Division in Erlang causes all integers to be cast to floating-point numbers (floats) first. Here’s a calculation that mixes
2 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
integers and floats: 7> 65 / 2.9. 22.413793103448278
But what if we want to do integer-only division? The
div
and
rem
operators come to our rescue:
8> 90 div 40. 2 9> 90 rem 40. 10
and like:
div
rem
only work with integers. Using a float will give us an error. Try this example so that you know what an error feels
10> 130.0 div 30 ** exception error: bad argument in an arithmetic expression in operator
div/2
called as 130.0 div 30
Erlang reports that a bad argument was present, which in our case is 130.0. Here is a table math operations that can be used: Type Description Works On + Addition Integers, Floats Subtraction Integers, Floats * Multiplication Integers, Floats / Floating-point division Integers, Floats div Integer Division Integers rem
Integer Remainder
Integers
2.3 Storing Values in Variables Erlang, like other languages, supports the notion of a variable as a way to store data and pass it around in a program. The one area where it differs is that, well, variables aren’t that variable. In fact, once you have bound a value to a variable it cannot be changed. Ever. This behaviour is one of the foundations of functional programming and is what makes Erlang programs so reliable. A variable cannot be changed once it is set - it is said to be immutable. Note: Variables in Erlang are immutable. The Handy Calculator is going to need variables to hold input values that will later be used inside calculations. Try the following example, and be sure to Capitalise the variable names. Note: Variables must begin with a Capital Letter. 1> FirstVar = 47. 47
In the first line of code [C→] [→C] My instruction counter has started from 1> again because I restarted my Erlang shell. Don’t worry if your counter is different. we are binding the value 47 to FirstVar. Looking at it, you may read it as: The value 47 is assigned to FirstVar. However, Erlang prefers to say: The unbound variable FirstVar is being bound to the value 47. Once a variable is bound, it cannot be re-bound to another value. Look what happens if we try and change FirstVar’s value: 2> FirstVar = 19. ** exception error: no match of right hand side value 19
It has already been bound to 47, causing the expression to become a comparison between 47 and 19. These two values are different and that’s why we get a “no match” error. [D→] [→D] The = symbol is actually a match operator, and is used to compare the left and right-hand side of an expression. We’ll come back to this when we get into Pattern Matching in NO-IDEA-YET. Note: Variables can only be bound once. Let’s create another variable and use them in a calculation 3> SecondVar = 23. 23 4> FirstVar + SecondVar. 70
We’ve created a second variable called (surprise surprise!) SecondVar and bound it to 23. The next expression adds SecondVar together to produce 70. Armed with what we’ve learned, we are just about ready to create our calculator!
3 -> 22
FirstVar
and
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
2.4 Functions: Reusable Pieces of Code We’re going to give our calculator the ability to add, subtract, multiply and divide any two numbers. This must be possible without knowing what the numbers are upfront, and can only be done through functions that take in variables. A function is a reusable unit of code that has zero or more input parameters and returns a result. Let’s create an adder function: 1> Add = fun(Num1, Num2) -> Num1 + Num2 end. #Fun<erl_eval.12.113037538> [E→] [→E] Don’t worry if the numbers after _eval will look a little different on your machine.
2> Add(10, 20). 30
I know I’ve just thrown a whole bunch of syntax at you, so allow me to show you how the pieces fit together and it will become clearer. Lets take the first line and break it up, bit by bit: Code What it does Add Declares a new, unbound variable = Binds the result of the right-hand-side to the Add variable fun(Num1, Starts a function with two input arguments Num2) ->
Marks the start of the function body [F→] [→F] Strictly speaking, -> stands for implies / such that. For example, Add is a function that takes in two parameters such that the result is Num1 + Num2. Num1 + Num2 Adds the two input arguments together end Marks the end of the function body . Ends the Erlang expression We now have an Add variable who’s value is a function. Being able to pass functions around as variable is much like being able to send nuggets of code around. If this feels a little strange, ask the Shell to tell you what the value of Add is: 3> Add. #Fun<erl_eval.12.113037538>
The Shell reports the value of Add to be a function. More correctly, we say that the variable code rocks! Note: Variables can hold functions too. There are three more functions to go, so let’s declare them now:
Add
is bound to a function. Reusable
4> Subtract = fun(Num1, Num2) -> Num1 - Num2 end. #Fun<erl_eval.12.113037538> 5> Multiply = fun(Num1, Num2) -> Num1 * Num2 end. #Fun<erl_eval.12.113037538> 6> Divide = fun(Num1, Num2) -> Num1 * Num2 end. #Fun<erl_eval.12.113037538>
Ooops! Divide has the wrong code inside its function and is multiplying two numbers instead of dividing them. But Erlang will not allow the variable to be updated, leaving us stuck in the mud. To fix this we must instruct the shell to forget that Divide ever existed and remove it from scope. Use the f(Variable) BIF [G→] [→G] BIF is short for Built-In-Function. A BIF is a commonly-used function available inside the Erlang Shell. to remove the Divide variable: 7> f(Divide). ok 8> Divide. * 1: variable ’Divide’ is unbound
As you can see, Erlang knows nothing about Divide once we have released it with the f() BIF. This will only work in the Shell and is really only for fixing up typos while entering in commands. We must now declare the correct Divide function: 9> Divide = fun(Num1, Num2) -> Num1 / Num2 end. #Fun<erl_eval.12.113037538>
Great! We have all four math functions and are ready to offer basic calculator operations. Try some of these functions in the shell: 10> Multiply(2, 10).
4 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
20 11> Multiply(-1, 100). -100 12> Subtract(80,50). 40 13> Divide(90, 3). 30.0 14> Add(700,700). 1400
2.5 Using Modules to Group Functions So, we’ve just declared a bunch of functions in the shell and tested them out. But if we lose power to the machine those functions are gone, and we will have to re-enter them before we’re able to do calculations. I’m going to show you how to use a Module to store your functions. They also serve as a neat way to group common functions together. So, as you may have guessed, our module is going to be called calculator.
2.5.1 Creating the Calculator Module Recall that in section2.1↑ you created a place to store your code. Go to that directory and create a new file called calculator.erl: ~$ cd ~/e4s/chapter2↑ ~$ gedit [H→] [→H] I’ve used gedit to edit my module, but feel free to use your favorite text editor instead. See the Appendix B↓ for a complete list of editors. calculator.erl
Editing On Other PlatformsAll you need at this stage is a simple text editor; nothin fancy. Here are a few suggestions depending on what operating system you’re on: Windows Notepad, Notepad++ http://notepad-plus.sourceforge.net Mac TextEdit, TextMate Linux Gedit, vi, Emacs More information about editors and IDEs can be found in Appendix B↓called Erlang Editors and IDEs. Put the following code in the first line of your file. -module(calculator).
A module must start with a module definition as shown in the code above. This is the minimum code needed to make this file a valid Erlang module. Save the file and go back to the Erlang Shell. Now, compile it using the c(ModuleName) BIF. 1> c(calculator). {ok,calculator}
If you get a tuple saying {ok,calculator} you have successfully compiled your first module! If you got errors instead then make sure that: the module definition is the first line in the calculator.erl file the module name and the file name are exactly the same. Note: The module definition must be the first line of the file. Note: The module name is case sensitive and must match the file name.
2.5.2 Adding A Function to the Calculator Module Great! At this point we must celebrate the fact that the calculator is 100% defect-free, but it falls a little short on the usefulness side. We can easily fix this by adding the four calculator functions to the module. Go back to calculator.erl and type up the subtract function: -module(calculator). -export([subtract/2]).
%% Subtracts two numbers subtract(Num1, Num2) -> Num1 - Num2.
The subtract function, like all functions in Erlang, is started with a lower-case letter. Note: A function must start with a lower-case letter. In line 2 we instruct the compiler to make subtract visible to outside callers by exporting the function. This is similar to marking a method as public in C++. Functions are not exported when they are only used internally. The name subtract/2 is short-hand for indicating a function called subtract that has an arity of two. Arity is another way to say how many parameters are required by a function. Erlang permits function overloads that differ by the number of input parameters. We only have one subtract function in this module, but we must still specify how many arguments it takes.
5 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
The subtract/2 function starts on line 5 and makes room for two input parameters. There’s no return statement because the return value is always the result of the very last expression in the function body. In this case the sum of Num1 and Num2 is returned. You may have noticed that we don’t put an end as the last statement of the function. In section 2.4↑ we were actually declaring a fun: a special type of function that is identified by a variable, not a module. Note: The body of a function ends with a period. Switch to the Erlang shell and compile the module again. 4> c(calculator). {ok,calculator}
Now call the subtract function in the calculator module: 5> calculator:subtract(30, 40). 10
A function call inside a module always follows the module:function format.
2.5.3 Add the Remaining Functions We’re still missing the add, multiply and divide functions. Go ahead and add them into the calculator module. -module(calculator). -export([subtract/2,add/2,multiply/2,divide/2]). %% Subtracts two numbers subtract(Num1, Num2) -> Num1 - Num2. %% Add two numbers add(Num1, Num2) -> Num1 + Num2. %% Multiply two numbers multiply(Num1, Num2) -> Num1 * Num2. %% Divide two numbers divide(Num1, Num2) -> Num1 / Num2.
Aaah! Our handy calculator is almost complete. Compile it in the shell and give it a whirl. 6> c(calculator). {ok,calculator} 7> calculator:add(50,60). 110 8> calculator:multiply(220,2). 440 9> calculator:divide(80,3). 26.666666666666668
2.5.4 What Happens When You Compile a Module By compiling the calculator module you are creating a BEAM [I→] [→I] Short for Bogdan/Björn’s Erlang Abstract Machine file that can be executed by the Erlang Virtual Machine (VM) much like Java .class files and .Net assemblies. The VM interprets these instructions and translates them into machine-level instructions. Your .erl file doesn’t need to be present to be able to use your module, only the .beam file is required.
2.6 Checking For Bad User Input Up to now we haven’t been concerned with ensuring the the input we get is valid in the context of the calculation. It doesn’t really matter for add, subtract and multiply as these functions can accept any integer or float value. But it’s a different story for divide... 1> calculator:divide(50,0). ** exception error: bad argument in an arithmetic expression in operator
’/’/2
called as 50 / 0
Ooops! We tried to divide by zero and got an exception with a cryptic “bad argument” exception. Ideally the user should be told that he can’t divide by zero, and this is what we’re going to implement right now using Guards. Open calculator.erl and modify the divide function to look like this: divide(Num1, Num2) when Num2 =/= 0 -> Num1 / Num2; divide(Num1, Num2) -> infinity.
A Guard is a clause that must be true before the body of a function is executed. It is evaluated based on the values of the input parameters. You can have the same function with the same arity [J→] [→J] Arity tells you how many input parameters a function has. but each one differs by the guard. We’ve added a guard around the first divide function to ensure that it only executes when Num1 is not zero. The =/= operator means “Not equal to”. If Num1 is zero the second function is called and we return an error. Erlang evaluates function from the top and enters the first one that matches. As a side note, have a look at the semi-colon at end of the divide function. This indicates that there is another variation of divide/2 to follow. The semi-colon separates function variations with the same name and arity. The last variation always ends in a period. Note: All function variations with the same name and arity must end with a semi-colon (;), except for the last one that ends in a period (.).
6 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
Let’s test it out by compiling calculator.erl and calling the divide function. 4> c(calculator). {ok,calculator} 5> calculator:divide(50,2). 25.0 6> calculator:divide(90,0). infinity
The calculator returns an atom called infinity when trying to divide by zero.
2.7 Summing More Than Two Numbers Let’s imagine for a hypothetical moment that a user wants to add, say, three numbers together. How would he do this? One possible implementation is to add another function, like so: ... add(Num1, Num2, Num3) -> Num1 + Num2 + Num3.
This add function of arity three solves the problem, but what happens when the user wants to add four numbers? Or five? It’s not practical for us to keep adding overloads, so we need to find something that allows users to add as many numbers together as they like.
2.7.1 Introducing Lists The list is a data structure that holds values in a sequence. Hop over to the Shell for a moment and try create one: 5> []. [] 6> [1,2,3]. [1,2,3] 7> [10, an_atom, 20.0]. [10, an_atom, 20.0]
A list is declared using square brackets and can hold zero or more elements that do not have to be of the same type. Looking at the code above: The first list is empty, the second has three integers, and the last list contains an integer, an atom and a float. Declare a list of integers and bind it to a variable called MyList: 7> MyList = [1,2,3,4]. [1,2,3,4]
Now, use the hd() and tl() BIFs to extract the first and last elements respectively: 8> hd(MyList). 1 9> tl(MyList). [2,3,4]
It’s common to work through lists one element at a time, just like we want to do for summing up a list of numbers. Erlang has a neat built-in construct for breaking a list up into a head and the rest [K→] [→K] Also called the Tail. The Head Also called the first element that lives at index position zero. The Rest This is always a list containing the elements from position 1 to N. It contains N - 1 elements. Let’s play around with this syntax to give you a better idea: 10> [Head | Rest] = MyList. [1,2,3,4] 11> Head. 1 12> Rest. [2,3,4]
On the first line we see Head and Rest as unbound variables that will be bound to the values being unpacked from the list in MyList. You don’t have to call them Head and Rest, but this seems to be a convention amongst Erlang programmers. The next expression shows that Head now contains 1, the first element in MyList. The remaining elements [2,3,4] are stored inside Rest. Try the expression again, this time using Rest on the right-hand-side:
7 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
13> [Head2 | Rest2] = Rest. Head2. 2 14> Rest2. [3,4]
See where this is going? We’re going to use this method to pull one element out at a time until the end of the list is reached. This syntax is called the cons, short for constructor. It can also be used in reverse to building up a list: 15> [Head2 | Rest2]. [2,3,4]
2.7.2 Using Recursion to Sum a List Erlang loves recursion [L→] [→L] Please see section 2.7.2↑ for an explanation of recursion. , and it is common practice for a function to call itself to process a set of data. Procedural languages, on the other hand, maintain a list of stack frames that is updated each time a function calls another. Recursive calls in these languages can quickly run out of stack space. Erlang functions are deterministic by their very nature because there is no concept of global shared state. Input data cannot be modified and only copied or new data is returned. It’s a good idea to restart your erlang shell at this point by pressing CTRL+C twice and then running erl: 1>
^C
BREAK:(a)bort (c)ontinue (p)roc info (i)nfo (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution ^C
~$ erl Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false] Eshell V5.7.2
(abort with ^G)
1>
Go to calculator.erl and type up the add/1 function: %% Add up a list of numbers add([Head | Rest]) -> Head + add(Rest).
Don’t forget to add it to the list of exports: -export([subtract/2,add/1,add/2,multiply/2,divide/2]).
Go back to the shell, compile your module and sum up a list of numbers: 1> c(calculator). {ok,calculator} 2> calculator:add([1,2,3]). ** exception error: no function clause matching calculator:add([]) in function calculator:add/1
Doh! It seems that calling calculator:add with an empty list didn’t go to the function that we’d just added. This is because Erlang can’t extract a Head and Rest from an empty list. To see what I mean, give it a try in the shell: 3> [Head | Rest] = []. ** exception error: no match of right hand side value []
You see, Erlang will pattern match the function call parameters to the actual function input parameters and enter the one that has a positive match. In this case, [Head | Rest] cannot be matched to an empty list [] and there were no other add/1 functions, so the runtime had to throw an error. We can fix this by inserting an add/1 function that caters for empty lists that returns zero when called. %% Add up a list of numbers add([Head | Rest]) -> Head + add(Rest); add([]) -> 0.
Don’t forget to end the first add/1 with a semi-colon (;). Compile the module and give it a second try: 14> c(calculator). {ok,calculator} 15> calculator:add([1,2,3]). 6 16> calculator:add([10,20,90,100]). 220
So how does this work? We can see by running through this example step-by-step. Step Input Head Rest Action Returns
8 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
a. [1,2,3,4] 1 [2,3,4] Add 1 to the sum of [2,3,4] 10 b. [2,3,4] 2 [3,4] Add 2 to the sum of [3,4] 9 c. [3,4] 3 [4] Add 3 to the sum of [4] 7 d. [4] 4 [] Add 4 to the sum of [0] 4 e. [] n/a n/a Return zero. 0 The call starts out on step a with the full list of numbers. The action calls for 1 to be added to the sum of 2, 3 and 4. This cycle continues until we have an empty input list and the second add/1 function is called, returning 0. We go back up the list, adding the result of each function call until we’re back at step a to return the final result of 10 to the caller.
2.8 What We Have Covered You’ve taken your first walk through the world of Erlang and looked mostly at how it behaves as a language. There is much, much more than just a bunch of interesting syntactical rules in Erlang and, as you’ll see later, it really shines when you see how the language and the framework combine to give you a top-class solution. Re-capping at the end of every chapter helps cement your understanding of core concepts. It’s also like a mental checklist that points out parts that may still be fuzzy in your mind. Go back to these parts, re-read the text and try the examples again. -- cross-reference the topics that have been covered.
3 Build a Grocery Store Our little supermarket is going to stock a few key items like bread, milk and beer. Initially we won’t have baskets, so customers will have to say how many items they want and pay for them straight away. Later on we’ll add a basket that customers must carry around with them. They will get a printed receipt and a total when they go to the checkout.
3.1 Before We Begin Prepare a new place on disk to hold your source code. Create a directory called “e4s/chapter3↑”: ~$ mkdir -p ~/e4s/chapter3↑ ~$ cd ~/e4s/chapter3↑ ~/e4s/chapter3↑$
Now start the Erlang shell: $ erl Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false] Eshell V5.7.2 (abort with ^G) 1>
3.2 Providing Groceries to Buyers We have three items to sell to customers: bread, milk and beer. We’re going to add three variants of the buy/2 function, and use an atom to identify what the customer wants. A what?? An atom! Remember how you’re taught not to use magic strings inside your code, but have the string stored in a macro or constant somewhere else? Well, an atom is an elegant way to have a constant that means the same thing everywhere. An atom always starts with a lower-case letter (unlike a variable that always starts with an upper-case letter). Go to the shell and create an atom called bread: 1> bread. bread
The value of the bread atom is itself, i.e. bread is equal to bread, no matter where the atom was declared or where it is running. They’re kind of like universal constants. Try matching bread to bread: 2> bread = bread. bread 3> GroceryItem = bread. bread 4> AnotherOne = bread. bread 5> GroceryItem = AnotherOne. bread
9 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
Here we’ve created variables GroceryItem and AnotherOne, bound them both to the bread atom and then match them to one another. We could also use an equality test to see if the values are equal: 6> GroceryItem == AnotherOne. true 7> GroceryItem == milk. false
The equality == operator will return true if the two terms are equal and false otherwise, both of which are actually atoms. Other languages like C++, for example, treat true and false as first-class language constructs whereas Erlang simply uses atoms. See section A.1↓ for a list of comparison operators.
Note: Use == to test if two terms are equal.
3.2.1 A Few Rules about Atoms Recall that in section 2.3↑ we said that a variable must start with a capital letter. The opposite is true for atoms: they must start with a lower-case letter: 8> bananas.
%% this will work
bananas 9> Bananas.
%% This will error because it’s an unbound variable
Note: Atoms must start with a lower-case letter. You can also use single quotes to create atoms: 10> ’Bananas’. ’Bananas’ 11> ’My atom has spaces!’. ’My atom has spaces!’
And you can include a few special characters: 12> atoms@here. atoms@here 13> an_atom@is_here. an_atom@is_here
3.3 Create the Shop Module Getting back to our grocery store, go to your editor and create a file called shop.erl so that we have a place to hold our code. Add 3 methods called buy/2, one for each grocery item. -module(shop). -export([buy/2]). buy(bread, Quantity) -> {bread, Quantity * 10.0}; buy(milk, Quantity) -> {milk, Quantity * 8.50}; buy(beer, Quantity) -> {beer, Quantity * 34.50}.
We’ve got three variants for buy/2, one for each product, simply so that we can work out the total price. Take note of a few things before compiling the module: Each function starts with an atom for the grocery. This allows us to use pattern matching to find the right function for the shopper. We only exported buy/2 once, even though we have three variants of this function. The first two variants end with a semi-colon, but the last one ends with a period. Right, now go ahead and compile shop.erl in your shell, and buy a few items: 14> c(shop). {ok,shop} 15> shop:buy(bread, 2). {bread,20.0} 16> shop:buy(beer, 1). {beer,34.5}
We bought two loaves of bread for R20,00 [M→] [→M] R is the symbol for Rands which is the currency in South Africa. 1 USD is about R7. and a six-pack of beer for R34.50. This example shows us the magic of pattern matching at work, and also reveals why atoms are so useful as meta information in your code. Calling buy(bread, 2) will send you straight to buy(bread, Quantity) because:
10 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
The number of input parameters matches The first input parameter matches (bread == bread) Using atoms is far more coder-friendly that having magic numbers to represent each grocery item.
3.4 Handle Unknown Grocery Items What happens if we try buy a newspaper? 17> shop:buy(newspaper, 1). ** exception error: no function clause matching shop:buy(newspaper,1)
We get an error because there is no function to take our order; the shop doesn’t keep newspapers. Our module must tell shoppers when a certain item is not stocked and print an appropriate error message. We’ll use a fourth variant of buy/2 as a catch-all for this scenario. Go to the bottom of shop.erl and add the fourth function: -module(shop). -export([buy/2]). buy(bread, Quantity) -> {bread, Quantity * 10.0}; buy(milk, Quantity) -> {milk, Quantity * 8.50}; buy(beer, Quantity) -> {beer, Quantity * 34.50}; buy(NotStocked, Quantity) -> io:format("Sorry, we don’t keep that.~n", []), {error,no_stock}.
Remember that the last function clause is the one that ends in a period while the other three all end in a semi-colon. In this case, the period sits at the end of the tuple {error,no_stock}. We’re also using the format/2 function in the io module to print a message to the console. The module:function syntax is used to call a function that lives in a module outside of shop. The ~n character is a special placeholder that is replaced with a new-line. Compile the module and see what happens when you try buy a newspaper again. 18> c(shop). ./shop.erl:10: Warning: variable ’NotStocked’ is unused ./shop.erl:10: Warning: variable ’Quantity’ is unused {ok,shop} 19> shop:buy(newspaper, 1). Sorry, we don’t keep that. {error,no_stock}
Great, we’re printing a user-friendly message instead of dying with an obtuse exception, but what about those warnings regarding NotStocked and Quantity? Erlang noted that we had variables being bound to values that were not being used in the function body. While this does not cause any errors, it’s wasteful to store data that’s never going to be used. But how do we keep our function’s arity and avoid wasting memory?
3.4.1 Wildcards and Underscore Variables There are two special types of variables that we can use to keep our arity: Wildcards A catch-all variable that will match anything. It is represented with an underscore. Underscore Variables A variable that starts with an underscore and a capital letter. It behaves like a normal variable, except that you cannot use it in your function. Let’s try each one in the console, starting with a simple match expression: 10> _ = foo. foo 11> _ = bar. bar 12> _ = {foo, bar} {foo, bar} 13> {_,_} = {foo, bar} {foo, bar}
So far so good, let’s try a few underscore variables: 14> _Foo = bar. bar 15> _Foo = bar. bar
We can bind to _Foo as many times as we like because Erlang will discard the variable as soon as the expression is complete. In other words, the _Foo variable has been forgotten by the time we get to line 15. Note: An underscore variable only lives for the duration of the expression it belongs to. Try the next example with the lifetime of an underscore variable in mind:
11 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
16> _Foo = {1, 2}. {1,2} 17> {_Foo, _Foo} = {3,4}. ** exception error: no match of right hand side value {3,4}
What happened here? Well, _Foo is mentioned twice in the same expression and so it follows the standard variable behaviour. Pattern matching will cause _Foo to be bound to 3 first. Next, Erlang will attempt to bind 4 to _Foo but is blocked by the immutability rule as mentioned in 2.3↑. The only way to make this expression work is by changing the second underscore variable to a different name: 15> {_Foo, _Bar} = {3,4}. {3,4}
So, with this in mind, it seems that all we need are wildcards on the last clauses. Go back to the shop module and change the last clause to use wildcards: -module(shop1). -export([buy/2]). buy(bread, Quantity) -> {bread, Quantity * 10.0}; buy(milk, Quantity) -> {milk, Quantity * 8.50}; buy(beer, Quantity) -> {beer, Quantity * 34.50}; buy(_, _) -> io:format("Sorry, we don’t keep that.", []), {error,no_stock}.
Our last clause has two wildcards represented by the underscores and will match when the user tries to buy something that is not stocked. Compile again and you’ll notice that there aren’t any warnings left: 20> c(shop). {ok,shop} 21> shop:buy(newspaper,1). Sorry, we don’t keep that.{error,no_stock}
You may be wondering why the the function clause with the wildcards went to the bottom of the file: it’s because the function clauses are matched from top to bottom. If we had put it at the top then none of our other clauses would match. To see what I mean, move buy(_,_) to the top of the file: -module(shop). -export([buy/2]). buy(_, _) -> io:format("Sorry, we don’t keep that.", []), {error,no_stock}; buy(bread, Quantity) -> {bread, Quantity * 10.0}; buy(milk, Quantity) -> {milk, Quantity * 8.50}; buy(beer, Quantity) -> {beer, Quantity * 34.50}.
Now compile the module in the shell: 5> c(shop). ./shop.erl:7: Warning: this clause cannot match because a previous clause at line 4 always matches ./shop.erl:9: Warning: this clause cannot match because a previous clause at line 4 always matches ./shop.erl:11: Warning: this clause cannot match because a previous clause at line 4 always matches {ok,shop}
Erlang detects that we’ve got a catch-all clause that will prevent the rest of the clauses beneath it from matching because function clauses are matched top-to-bottom. Any purchases will give us the “no stock” message: 6> shop:buy(beer,2). Sorry, we don’t keep that.{error,no_stock}
Note: Function clauses are matched top-to-bottom. Change your module back so that the catch-all is the last function clause.
3.5 Catering for Zero-Item Purchases Our grocery store is almost complete except for one, time-wasting problem: Shoppers are free to purchase zero of an item, holding up the cashiers in the process. We must add a function clause that will prevent shoppers from buying zero of something, but still allow valid purchases to go through. Go back to shop.erl and add a new function clause above all others: -module(shop). -export([buy/2]). buy(Item, 0) -> io:format("You cannot buy zero of ~p~n", [Item]), {error, invalid_quantity); buy(bread, Quantity) -> {bread, Quantity * 10.0}; buy(milk, Quantity) -> {milk, Quantity * 8.50}; buy(beer, Quantity) -> {beer, Quantity * 34.50}. buy(_, _) -> io:format("Sorry, we don’t keep that.", []), {error,no_stock};
We’ve added a clause that has a variable called What and a zero as the function parameters. Compile the module and try buy zero beers: 10> c(shop). {ok,shop} 11> shop:buy(beer, 0). You cannot buy zero of beer {error,invalid_quantity}
12 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
3.5.1 Formatting Strings with io:format/2 Whew, with that potential disaster averted, let’s take a closer look at io:format/2 that we used to print out the item that the shopper wanted to buy. io:format/2 lets us bake in Erlang values which are printed to the console and is handy for outputting debug messages in your program. It takes in a template string with placeholders peppered in-between as well as a list of values and match them to produce a formatted string. Here’s a simple example: 12> io:format("Erlang ~p formatting ~p!~n", [string,rocks]). Erlang string formatting rocks ok
Each ~p is a positional placeholder that is replaced by the corresponding value in the list, no matter what the type The first ~p is replaced with the string atom and the second is replaced by the rocks atom. We ended our formatting with the new-line symbol ~n and it was followed by the ok atom which is the output of the format/2 function. Note: The number of placeholders must equal the number of elements in the list, except for ~n. Here’s a list of commonly-used placeholders: ~p Replaced by any Erlang term and will do pretty printing. ~s A string. ~n A new-line. Use this to print many lines inside one format/2 call Try a few examples in your shell: 1> io:format("A tuple: ~p~n", [{one,2,three,4.0}]). A tuple: {one,2,three,4.0} ok 2> io:format("The rain in Spain ~s~n",["is wet"]). The rain in Spain is wet ok 3> io:format("A string: ~p~n", ["shown as a term"]). A string: "shown as a term" ok
The last example spits out our string in exactly the same way that we entered it, showing how the print a term in the same way that you would type it out in the shell.
~p
placeholder works: It will
3.6 Handling Bulk Orders There are a few big spenders who know exactly what they want to order without needing to browse the shelves. They would like to hand over a list of items and quantities and have the whole order processed at once. In Erlang terms this means giving us a list of elements where each element describes the stock item and the quantity. But how can we hold two values at once? One way is to use a tuple, which is a fixed-size collection of elements that can be any type. It starts and ends with curly brackets, like this: 1> {1,2,three}. {1,2,three}. 2> MyTuple = {can, hold, "anything"}. {can,hold,"anything"}.
3.6.1 The Shopping List Customers with big orders will need to send in a list of tuples where the first element is the item and the second is how many items they wish to buy. It must cater for duplicate items and ignore those that don’t exist. We’re going to add a method called bulk_order/1 that accepts this list and starts processing it.
3.7 What We Have Covered List the topics that have been covered in this chapter.
4 Single-Player Rock, Paper, Scissors The sacred game of Rock-Paper-Scissors has been the cornerstone of decision-making for many thousands of years. Wars have been won and lost over this game, whole countries have changed hands over the outcome of this most respected past time [N→] [→N] The validity of these claims is neither confirmed, denied or avoided. . We will continue this journey down nonsensical outcomes as we implement our very own version in Erlang. In this chapter we focus on Erlang’s ecosystem by looking at libraries, help documentation, debugging and command-line tools.
13 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
4.1 Before We Begin Prepare a new place on disk to hold your source code. Create a directory called “e4s/chapter4↑”: ~$ mkdir -p ~/e4s/chapter4↑ ~$ cd ~/e4s/chapter4↑ ~/e4s/chapter4↑$
Now start the Erlang shell: $ erl Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false] Eshell V5.7.2 (abort with ^G) 1>
4.2 Analysing The Game Rock-Paper-Scissors is played by two people and takes one turn to determine the outcome. Each player faces the other with his hand behind his back. The hidden hand can take the form of a rock, a sheet of paper or a pair of scissors. On the count of three both players bring their hand to the front to reveal their chosen weapon and the result is decided. Rock Paper Scissors Rock draw paper rock Paper paper draw scissors Scissors rock scissors draw Table 4.1 Rock-Paper-Scissors Winning Combinations Table 4.1↑ shows the winning weapon for each possible combination. For example, Paper wins over Rock but loses to Scissors. For the purposes of this chapter we are going to assume that the other player is always the computer, and that a result is determined using a random number generator.
4.3 The Game Module: rpsgame Let’s think a little on how this will work for the player. He starts a game by calling the play function with his attack. The game randomly picks a response for the computer and then returns the result to the player. Hypothetically, these steps will look like this: 1> rpsgame:play(rock). {win, {player, rock}, {computer, scissors}} 2> rpsgame:play(paper). {lose, {player, paper}, {computer, rock}} 3> rpsgame:play(scissors). {draw, {player, scissors}, {computer, scissors}}
The computer’s weapon is hard-coded to rock for now. Wins and losses are always declared from the player’s perspective. Create a new file called rpsgame.erl using your text editor and add the following code: -module(rpsgame). -export([play/1]). %% Play a game of rock-paper-scissors play(PlayerAttack) -> ComputerAttack = rock, %% computer attack is hard-coded Result = get_result(PlayerAttack, ComputerAttack), {Result, {player, PlayerAttack}, {computer, ComputerAttack}}. %% Determine the result of an attack get_result(PlayerAttack, ComputerAttack) -> case {PlayerAttack, ComputerAttack} of {rock, scissors} -> win; {paper, rock} -> win; {scissors, paper} -> win; {Same, Same} -> draw; {_,_} -> lose end.
Looking at get_result/2 we see that it uses a case statement to work out if the player has won or lost. The two attacks are combined into a tuple and then matched to the appropriate case clause. The first three clauses are for when a player has won. The fourth clause {Same, Same} matches when the player and the computer have the same attack, and the last clause {_, _} is a catch-all for all remaining combinations in which the player has lost. Compile your module and call the play function: 4> c(rpsgame). {ok,rpsgame} 5> rpsgame:play(rock). {draw, {player, rock}, {computer, rock}} 6> rpsgame:play(paper). {win, {player, paper}, {computer, rock}}
4.3.1 Pattern Matching The Same Variable Twice You may be wondering how something like {Same, Same} actually works, given that section 2.3↑ told us that variables can only be bound once and are immutable.
14 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
The answer lies with how the match operator behaves on bound and unbound variables. Recall that when you first store a value in a variable, you are actually binding a value to an unbound variable, like this: 3> MyVar = rock. rock
MyVar starts out as being an unbound variable that is subsequently bound to rock. Thereafter, we can’t change the value of MyVar, but we can certainly match it back to the value it contains: 4> MyVar = paper. %% can’t do this ** exception error: no match of right hand side value paper 5> MyVar = rock. %% but this will work rock
The last statement in which we match MyVar to rock is valid because MyVar’s value is the same as rock. So, how does this help with our {Same,Same} case clause? Let’s imagine that the input to get_result/2 is {paper, paper}. Since we know that Same starts out as an unbound variable, it will get bound to paper and all is well. The second occurance of Same is now bound to paper, and so it matches to the second input value of paper. Another way to experiment with this feature is by trying these examples: 6> {Same,Same} = {foo,foo}. {foo,foo} 8> Same. foo 7> {NotSame,NotSame} = {bar,baz}. ** exception error: no match of right hand side value {bar,baz}
The last line failed because NotSame was already bound to bar and could not be matched to baz.
4.4 Randomising the Computer’s Attack While it’s fun playing an opponent who give rocks about giving rocks [O→] [→O] “To give rocks” is a South African slang term for being indifferent. See http://en.wikipedia.org/wiki/List_of_South_African_slang_words , the novelty soon wears thin. We’re going to use one of Erlang’s standard library functions to get a random attack from the Computer. Erlang comes equipped with a rich collection of useful modules and functions called the Standard Library that available to all Erlang environments regardless of the host operating system. We’re going to make use of the random [P→] [→P] Read up about the random module online at http://www.erlang.org/doc/man/random.html module to give us a number between 0 and 1. Go back to rpsgame.erl and add a function called get_computer_attack: ... %% choose a computer attack at random get_computer_attack() -> %% Get an index position at random Index = random:uniform(3), %% Pull out an attack lists:nth(Index, [rock, paper, scissors]). ...
The first statement is where we call the uniform/1 function in the random module. This is the way you call functions in Erlang. There are no namespaces to import or references to include. Note: Module:Function is the standard way to call a function. The next line takes an element from the list at position Index, and this becomes the computer’s attack. Note: The lists module refers to the first element as index 1. We’re not finished yet: get_computer_attack/0 is going to be called from the play/1 function. Go back to play/1 and add it in: ... play(PlayerAttack) -> ComputerAttack = get_computer_attack(), Result = get_result(PlayerAttack, ComputerAttack), {Result, {player, PlayerAttack}, {computer, ComputerAttack}}.
The ComputerAttack variable is now bound to the output of get_computer_attack/0 to give our eager player a fair chance at victory. Go back to your shell, compile the module and give it a try. 6> c(rpsgame). {ok,rpsgame} 7> rpsgame:play(rock). {lose,{player,rock},{computer,paper}} 8> rpsgame:play(rock). {win,{player,rock},{computer,scissors}} 9> rpsgame:play(rock). {win,{player,rock},{computer,scissors}}
4.5 Tracking Errors with Stack Traces Before we get into this, I want you to see what a stack trace [Q→] [→Q] also called a back-trace. looks like. Try attacking the computer with a banana: 8> rpsgame:play(banana).
15 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
** exception error: no case clause matching banana in function
rpsgame:get_result/2
in call from rpsgame:play/1
The first line tells us the error that caused our program to crash. The next two lines draw a path to the offending code starting from the inside-out. We read the stack trace like this: 1. Ah! Bananas are not allowed! (perhaps they’re too dangerous [R→] [→R] A banana is a weapon not to be trifled with, according to the great Monty Python. ) 2. The error ocurred in the get_result/2 function, which is in the rpsgame module 3. That function was called by rpsgame:play/2 (but this is not where the error happened) The second line of a stack trace always tells you exactly where the problem happened. This error occurred in the get_result/2 function because our game doesn’t know about the banana attack. Unfortunately line numbers are not compiled into the beam leaving us with no way to get the line number of the code that cause the error. We’ll need to do input validation to prevent this from happening again. First add all_attacks/0 to the bottom of the module: %% Get all possible attacks all_attacks() -> [rock, paper, scissors].
This function returns a list of atoms, each one representing a valid attack. Using one-liner functions like this is a handy way to store constants in your modules. Modify play/1 to validate the user’s attack before continuing: %% Play a game of rock-paper-scissors play(PlayerAttack) -> case lists:member(PlayerAttack, all_attacks()) of true -> ComputerAttack = get_computer_attack(), Result = get_result(PlayerAttack, ComputerAttack), {Result, {player, PlayerAttack}, {computer, ComputerAttack}}; false -> {error, invalid_attack} end.
We use lists:member/2 to establish whether the input element belongs to the list of all possible attacks, as returned by all_attacks/0. Instead of crashing with a stack trace, we will return a tuple indicating that the user tried to send an unrecognised attack. Compile rpsgame and try attacking with bananas again: 4> c(rpsgame). {ok,rpsgame}. 5> rpsgame:play(banana). {error, invalid_attack} 6> rpsgame:play(rock). {draw,{player,rock},{computer,rock}}
Much better.
4.6 Using the Debugger We’re going to use Erlang’s interactive debugger to step through the rpsgame module. The debugger has all the attributes you’d expect such as setting breakpoints, stepping through code, changing values at runtime and examining the local stack inside a function call. Go into the erlang shell and fire up the debugger: 4> debugger:start().
The Monitor window will appear, looking similar to the one shown in Figure 4.1↓. Open the rpsgame module by going to Module ▷ Interpret..., select rpsgame.erl and click Choose and then Done. The module will appear in the list on the left-hand side.
Figure 4.1 The Monitor Window Select rpsgame and tick the checkbox labelled First Call below. Go back to the shell and play a game:
16 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
5> rpsgame:play(rock).
The Attach Process window appears and shows the current line of execution in bold, as in Figure 4.2↓.
Figure 4.2 Attach Process
4.6.1 Using the Debugger Toolbar The toolbar helps you navigate through the code during execution. Here is a list of the buttons and what they do: Step Move to the next line of code and step into a function call if there is one. Next Move to the next line of code and stay within the current function. Continue Carry on executing code until the next breakpoint. Finish Finish up executing this function and go back to the caller. Where Go to the current line of execution. This is handy if you’ve browsed to another part of your program. Up Go up to the function that called this one. Down Go down to the function being called. It’s a good idea to get used to Where, Up and Down, so let’s give it a shot. With the Attach Process window open and the first line of play/1 in bold, click the Step button to go into all_attacks/0.
Figure 4.3 Inside all_attacks/0 In figure 4.3↑ the debugger has stopped on the first line of all_attacks/0 and the Where and Up buttons are enabled. Clicking Up will go back to play/1, and clicking Where will return you back to all_attacks/0. Use the Finish button complete the execution of all_attacks/0 and return to the calling function. Click Continue to run to the end of your program.
4.6.2 How to Set a Breakpoint Setting a breakpoint done by double-clicking a line of code in the Attach Window after it has come up showing your module code. Assuming that the debugger is still running and the Monitor window is still showing, go back to your shell and play another
17 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
game of rock-paper-scissors: 6> rpsgame:play(paper).
The Attach Process window will come up showing the first line of play/1 in bold. Double click the line below it and see it change to red, indicating that it has a breakpoint set. Figure 4.4↓ has a breakpoint on line 8. Click Continue to run to this breakpoint.
Figure 4.4 Setting a Breakpoint
4.6.3 Changing Variables At Runtime The debugger lets you change a variable’s while stepping through your code. Figure 4.5↓ shows two variables in the current stack while we’re executing play/1, namely ComputerAttack and PlayerAttack.
Figure 4.5 Two Variables in the Stack Double-clicking one of these shows a popup that lets you change the variable’s value to any valid Erlang term.
Figure 4.6 Changing a Variable
4.7 A Tour of Erlang’s Help System Getting to know the Erlang help system will go a long way in helping you become a productive coder, and it doesn’t hurt that their documentation is clear and full of handy examples. There are several ways to get reference information, both offline and online.
4.7.1 Understanding Erlang’s Documentation. The usage syntax can be a little cryptic at first but, with a little guidance, it will become a very useful part of your coding toolbox. Open the reference page for the lists module, found at http://erlang.org/doc/man/lists.html The page starts with a summary and description of the module and then lists each function in alphabetical order under the Exports section. Each function describes the input parameter and return value, including how each of these values are structured. Example: lists:any The documentation describes lists:any/2 as follows: any(Pred, List) -> bool() Types: Pred = fun(Elem) -> bool() Elem = term() List = [term()] Returns true if Pred(Elem) returns true for at least one element in List.
The first line is the spec of the function and details what arguments it expects and what will be returned at the end of the call. The bool() shown at the end of the line is a predefined type for either the true or false atom. These predefined types are part of
18 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
Erlang’s Type Notation and are merely ways to describe values. It is used primarily for documentation purposes so that readers may understand what values a function accepts and returns. The next lines under the Types heading describes all possible values for Pred and List: Pred must be a function of arity 1 that returns either true or false and accepts any valid Erlang term (indicated by the term() predefined type). List can be a list of elements, where each element is any valid Erlang term. All types that are described using the Erlang Type Notation eventually resolve down to a predefined type. Think of these like elements in the periodic table: all matter is composed of one or more elements taken from the periodic table [S→] [→S] Chuck Norris, however, refutes the Periodic Table in favour of the only element he believes in: Surprise. . Table 4.2↓ lists all predefined types. Name Meaning Alias any() Any valid Erlang type. term() atom(), binary(), float() Primitive data types. function(), integer() Primitive data types. pid(), port(), reference() Primitive data types. bool() An atom; either true or false. char() Represents a character. iolist() Recursively defined as [char() | binary() | iolist()]. tuple() A tuple. list(L) A list with elements of type L. [L] nil() An empty list. [] string() A list of chars. list(char()) deep_string() Recursively defined as [char() | deep_string()]. none() Not a type, but used for functions that never exit. Table 4.2 Predefined Types
4.7.2 Finding Help On a Module or Function Online Help Erlang documentation is readily available online. Follow these steps to get to the module and function you’re looking for: Go to http://erlang.org/doc/ Click on the Modules link on the left-hand side You will be taken to the full index of all Erlang modules Click on the module name to go to the documentation page Windows Users The HTML documentation is installed on your system as part of the Installation Wizard. Go to Start ▷ All Programs ▷ Erlang OTP ▷ Erlang Documentation. It will open the root documentation page in your browser. Click on the Modules link on the left-hand side to get a full list of all modules in the standard library. Select the module you are interested in. Linux Users The best way to view the documentation is via the Erlang MAN pages. I’m using Ubuntu’s apt-get package management system it may be a little different if you’re not using a Debian-based Linux distribution. Open up a Terminal window and type: $ sudo apt-get install erlang-manpages
Once that’s complete, try finding help on the lists module: $ erl -man lists lists(3erl) ERLANG MODULE DEFINITION lists(3erl) NAME lists - List Processing Functions DESCRIPTION This module contains functions for list processing. The functions are organized in two groups: those in the first group performa particular operation on one or more lists, whereas those in the second group are higher- order functions, using a fun as argument to perform an...
Tip: Type /keyword and press enter to search for keywords within the man page.
4.8 More about Modules There are a few useful things you ought to know about Erlang modules.
4.8.1 Modules are Self-Describing Every Erlang module has a method called module_info/0 that tells you: What functions are exported What modules and functions are imported When and where the module was compiled
19 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
What version the module is at Any other user-defined attributes. To see this in action, jump back to the shell and call rpsgame:module_info/0 1> rpsgame:module_info(). [{exports,[{play,1},{module_info,0},{module_info,1}]}, {imports,[]},
{attributes,[{vsn,[111288562672827733269432973074155061690]}]},
{compile,[{options,[]}, {version,"4.6.2"},
{time,{2009,11,26,13,37,13}},
{source,"/home/luke/writing/e4s/e4s/code/ch_single_rps/rpsgame3.erl"}
]}]
You can call this method on any module, even those that belong to the standard library.
4.8.2 Code to Extract Version Information Here’s a useful module called modver which will spit out the version number of a module: -module(modver). -export([get_version/1]). %% Returns the version of a given module get_version(Module) -> ModuleInfo = Module:module_info(), {_, Attributes} = lists:keyfind(attributes, 1, ModuleInfo), {_, [Version]} = lists:keyfind(vsn, 1, Attributes), Version.
Calling this module is straight-forward. Try getting the version number of the lists module. 1> c(modver). {ok,modver} 2> modver:get_version(lists). 216324515770692103956749987828157165782
What about running it on our modver module? 3> modver:get_version(modver). 119608875777930581568409021999433264669
That’s quite a long number and won’t be of much help for anyone wanting to make sure they’ve got the right version. The next section talks about attributes and how to specify version numbers explicitly.
4.8.3 Versioning and Module Metadata You can bake metadata into a module by adding an attribute to the top of the source file that follows this format: -Tag(Value).
There are a few predefined attributes that Erlang looks out for and are described here: -module(Module). The module declaration. -export(Functions). A list of functions available to outside callers. -import(Module,Functions). A list of functions that do not need to be fully qualified when called inside this module. -compile(Options). Additional compiler options. -vsn(Version). The module version. Can be any literal term. Go to modver.erl and give it a version: -module(modver). -export([get_version/1]). -vsn(1.3). %% Returns the version of a given module get_version(Module) -> ModuleInfo = Module:module_info(), {_, Attributes} = lists:keyfind(attributes, 1, ModuleInfo), {_, [Version]} = lists:keyfind(vsn, 1, Attributes), Version.
Now call get_version/1 on modver: 4> c(modver). {ok,modver} 5> modver:get_version(modver). 1.3
All attributes in the code file will be compiled into the beam and can be retrieved using module_info/1. Let’s add an attribute specifying the weather at the time we wrote this module: -module(modver). -export([get_version/1]). -vsn(1.3). -weather(sunny). %% Returns the version of a given module get_version(Module) -> ModuleInfo = Module:module_info(), {_, Attributes} = lists:keyfind(attributes, 1, ModuleInfo), {_, [Version]} = lists:keyfind(vsn, 1, Attributes), Version.
Compile this module and call modver:module_info/0 to see the attribute appear: 6> c(modver). {ok, modver} 7> modver:module_info(). [{exports,[{get_version,1},{module_info,0},{module_info,1}]}, {imports,[]},
{attributes,[{vsn,[1.3]},{weather,[sunny]}]}, {compile,[{options,[]},
{version,"4.6.2"},
20 -> 22
{time,{2009,11,26,14,51,19}},
{source,"/home/luke/writing/e4s/e4s/code/ch_single_rps/modver3.erl"}]
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
}]
5 Write a PNG File Parser Up to now we’ve spent most of our time getting used to Erlang as a language and haven’t done a lot outside of our program. I’m talking about accessing external resources such as files and directories outside of the Erlang Virtual Machine. In addition, I want to show you how Erlang makes programming binary protocols simple using bit syntax. Our PNG parser is going to: Traverse the filesystem to look for files Extract the image size and comment Do all of these things in parallel Report the job progress every few seconds. Let’s get parsing!
5.1 Find All PNG Files We’re going to start by finding all .png files in the current directory and return them in a list. Create a new module called pngparse.erl, open it in your editor and add the following code:
6 Multi-Player Rock, Paper, Scissors You’ve come a long way on this Erlang adventure, but here’s where the fun really starts. We’re going to show off Erlang’s robust services framework by writing a multi-player network game of Rock, Paper, Scissors. Just to lay it out for you, we’re going to: Create the Rock-Paper-Scissors (RPS) game engine Create the RPS server Here we start introducing network-connected services in a trusted node cluster. Maybe we can do some TCP here too. At some point I want to bring in OTP, but not sure when, or if it should be for another chapter. Logging would be nice to have here. This is where we start looking at using OTP behaviours as the basis for our service.
7 Write a Web Service using Mochiweb So, this is an HTTP-based service using Mochiweb that will feed bits of information. Maybe it’s a super-simple public key store that lets you save stuff using a key and pick it up later. How about introducing MNESIA at this point, to show what data storage is available in Erlang? OTP, if not shown already, must be introduced here. And there must be a fail-over example, with a discussion around what supervisor tree configurations are available.
8 Hanging References! Stuff that I need to cross reference to somewhere
8.1 Function Bodies Separating statements in a function. Using commas to separate them. Using semi-colon to separate multiple of the same arity. Using fullstop to end a functoin.
8.2 Nested Case Statements Nesting case statements. How to end the last nested case. What about Joe Armstrongs gotcha case.
8.3 Wildcard vs Underscore Variables Gotcha {_, _} = {1,2} will work. But {_Foo, _Foo} = {1,2} will result in a bad match because _Foo is a real variable that is already bound.
A Erlang Data Structures A.1 Comparison Operators
21 -> 22
08/08/2011 01:20 PM
Erlang for Skeptics rev 31
http://www.erlangforsceptics.com/book/
List all comparison operators and show a few examples of usage.
B Erlang Editors and IDEs Compare things like: Inline shell intellisense Templates for gen_server etc code syntax highlighting Error message to code-line Linux GNU Emacs Default erlang addons Better, addons from erlang consulting company ErlIDE (with Eclipse) Netbeans? Apple Mac ErlIDE (with Eclipse) Netbeans? Windows Notepad ++ Scite / Scintilla GNU Emacs ErlIDE (with Eclipse) Netbeans? Copyright (C) 2009 Luke Venediger
Erlang for Skeptics by Luke Venediger is licensed under a Creative Commons Attribution 3.0 United States License
22 -> 22
08/08/2011 01:20 PM