You are previewing Erlang Programming.

Erlang Programming

Cover of Erlang Programming by Simon Thompson... Published by O'Reilly Media, Inc.
  1. Erlang Programming
    1. SPECIAL OFFER: Upgrade this ebook with O’Reilly
    2. Foreword
    3. Preface
      1. Francesco: Why Erlang?
      2. Simon: Why Erlang?
      3. Who Should Read This Book?
      4. How to Read This Book
      5. Conventions Used in This Book
      6. Using Code Examples
      7. Safari® Books Online
      8. How to Contact Us
      9. Acknowledgments
    4. 1. Introduction
      1. Why Should I Use Erlang?
      2. The History of Erlang
      3. Erlang’s Characteristics
      4. Erlang and Multicore
      5. Case Studies
      6. How Should I Use Erlang?
    5. 2. Basic Erlang
      1. Integers
      2. The Erlang Shell
      3. Floats
      4. Atoms
      5. Booleans
      6. Tuples
      7. Lists
      8. Term Comparison
      9. Variables
      10. Complex Data Structures
      11. Pattern Matching
      12. Functions
      13. Modules
      14. Exercises
    6. 3. Sequential Erlang
      1. Conditional Evaluations
      2. Guards
      3. Built-in Functions
      4. Recursion
      5. Runtime Errors
      6. Handling Errors
      7. Library Modules
      8. The Debugger
      9. Exercises
    7. 4. Concurrent Programming
      1. Creating Processes
      2. Message Passing
      3. Receiving Messages
      4. Registered Processes
      5. Timeouts
      6. Benchmarking
      7. Process Skeletons
      8. Tail Recursion and Memory Leaks
      9. A Case Study on Concurrency-Oriented Programming
      10. Race Conditions, Deadlocks, and Process Starvation
      11. The Process Manager
      12. Exercises
    8. 5. Process Design Patterns
      1. Client/Server Models
      2. A Process Pattern Example
      3. Finite State Machines
      4. Event Managers and Handlers
      5. Exercises
    9. 6. Process Error Handling
      1. Process Links and Exit Signals
      2. Robust Systems
      3. Exercises
    10. 7. Records and Macros
      1. Records
      2. Macros
      3. Exercises
    11. 8. Software Upgrade
      1. Upgrading Modules
      2. Behind the Scenes
      3. Upgrading Processes
      4. The .erlang File
      5. Exercise
    12. 9. More Data Types and High-Level Constructs
      1. Functional Programming for Real
      2. Funs and Higher-Order Functions
      3. List Comprehensions
      4. Binaries and Serialization
      5. References
      6. Exercises
    13. 10. ETS and Dets Tables
      1. ETS Tables
      2. Dets Tables
      3. A Mobile Subscriber Database Example
      4. Exercises
    14. 11. Distributed Programming in Erlang
      1. Distributed Systems in Erlang
      2. Distributed Computing in Erlang: The Basics
      3. The epmd Process
      4. Exercises
    15. 12. OTP Behaviors
      1. Introduction to OTP Behaviors
      2. Generic Servers
      3. Supervisors
      4. Applications
      5. Release Handling
      6. Other Behaviors and Further Reading
      7. Exercises
    16. 13. Introducing Mnesia
      1. When to Use Mnesia
      2. Configuring Mnesia
      3. Transactions
      4. Partitioned Networks
      5. Further Reading
      6. Exercises
    17. 14. GUI Programming with wxErlang
      1. wxWidgets
      2. wxErlang: An Erlang Binding for wxWidgets
      3. A First Example: MicroBlog
      4. The MiniBlog Example
      5. Obtaining and Running wxErlang
      6. Exercises
    18. 15. Socket Programming
      1. User Datagram Protocol
      2. Transmission Control Protocol
      3. The inet Module
      4. Further Reading
      5. Exercises
    19. 16. Interfacing Erlang with Other Programming Languages
      1. An Overview of Interworking
      2. Interworking with Java
      3. C Nodes
      4. Erlang from the Unix Shell: erl_call
      5. Port Programs
      6. Library Support for Communication
      7. Linked-in Drivers and the FFI
      8. Exercises
    20. 17. Trace BIFs, the dbg Tracer, and Match Specifications
      1. Introduction
      2. The Trace BIFs
      3. Tracing Calls with the trace_pattern BIF
      4. The dbg Tracer
      5. Match Specifications: The fun Syntax
      6. Match Specifications: The Nuts and Bolts
      7. Further Reading
      8. Exercises
    21. 18. Types and Documentation
      1. Types in Erlang
      2. TypEr: Success Types and Type Inference
      3. Documentation with EDoc
      4. Exercises
    22. 19. EUnit and Test-Driven Development
      1. Test-Driven Development
      2. EUnit
      3. The EUnit Infrastructure
      4. Testing State-Based Systems
      5. Testing Concurrent Programs in Erlang
      6. Exercises
    23. 20. Style and Efficiency
      1. Applications and Modules
      2. Processes and Concurrency
      3. Stylistic Conventions
      4. Coding Strategies
      5. Efficiency
      6. And Finally...
    24. A. Using Erlang
      1. Getting Started with Erlang
      2. Tools for Erlang
      3. Where to Learn More
    25. Index
    26. About the Authors
    27. Colophon
    28. SPECIAL OFFER: Upgrade this ebook with O’Reilly
O'Reilly logo

Erlang’s Characteristics

Although Erlang on its own is an attractive programming language, its real strength becomes apparent when you put it together with the virtual machine (VM) and the OTP middleware and libraries. Each of them contributes to making software development in Erlang special. So, what are the features of Erlang that differentiate it from many of its peers?

High-Level Constructs

Erlang is a declarative language. Declarative languages work on the principle of trying to describe what should be computed, rather than saying how this value is calculated. A function definition—particularly one that uses pattern matching to select among different cases, and to extract components from complex data structures—will read like a set of equations:

area({square, Side})   ->    Side * Side ;
area({circle, Radius}) ->    math:pi() * Radius * Radius.

This definition takes a shape—here a square or a circle—and depending on which kind of shape it receives, it matches the correct function clause and returns the corresponding area.

In Erlang, you can pattern-match not only on high-level data but also on bit sequences, allowing a startlingly high-level description of protocol manipulation functions. Here is the start of a function to decode TCP segments:

decode(<< SourcePort:16, DestinationPort:16,
          SequenceNumber:32,
          AckNumber:32,
          DataOffset:4, _Reserved:4, Flags:8, WindowSize:16,
          Checksum:16, UrgentPointer:16,
          Payload/binary>>) when DataOffset>4 ...

In the preceding code, each numeric length, such as 4 in DataOffset:4, gives the number of bits to be matched to that variable. By comparison, think of how you would achieve the same effect in C or Java.

Another aspect of Erlang is that functions (or closures) are first-class data. They can be bound to a variable and can be treated just like any other data item: stored in a list, returned by a function, or communicated between processes.

List comprehensions, also taken from the functional programming paradigm, combine list generators and filters, returning a list containing the elements of the list generators after the filters have been applied. The following example of list comprehensions, which we explain fully in Chapter 9, is an implementation of the quicksort algorithm in a couple of lines of code:

qsort([]) -> [];
qsort([X|Xs]) ->
    qsort([Y || Y<-Xs, Y =< X]) ++ [X] ++ qsort([Y || Y<-Xs, Y > X]).

Concurrent Processes and Message Passing

Concurrency in Erlang is fundamental to its success. Rather than providing threads that share memory, each Erlang process executes in its own memory space and owns its own heap and stack. Processes can’t interfere with each other inadvertently, as is all too easy in threading models, leading to deadlocks and other horrors.

Processes communicate with each other via message passing, where the message can be any Erlang data value at all. Message passing is asynchronous, so once a message is sent, the process can continue processing. Messages are retrieved from the process mailbox selectively, so it is not necessary to process messages in the order they are received. This makes the concurrency more robust, particularly when processes are distributed across different computers and the order in which messages are received will depend on ambient network conditions. Figure 1-1 shows an example, where an “area server” process calculates areas of shapes for a client, as we did earlier in High-Level Constructs.

Communication between processes

Figure 1-1. Communication between processes

Scalable, Safe, and Efficient Concurrency

Erlang concurrency is fast and scalable. Its processes are lightweight in that the Erlang virtual machine does not create an OS thread for every created process. They are created, scheduled, and handled in the VM, independent of the underlying operating system. As a result, process creation time is of the order of microseconds and independent of the number of concurrently existing processes. Compare this with Java and C#, where for every process an underlying OS thread is created: you will get some very competitive comparisons, with Erlang greatly outperforming both languages.

Erlang processes communicate with each other through message passing. Regardless of the number of concurrent processes in your system, exchanging messages within the system takes microseconds. All that is involved in message passing is the copying of data from the memory space of one process to the memory space of the other, all within the same virtual machine. This differs from Java and C#, which work with shared memory, semaphores, and OS threads. Even here, benchmarks show that Erlang manages to outperform these languages for the same reasons it outperforms them in process creation times.

You might think that comparing Erlang to C# and Java is unfair to these two languages, as we are comparing apples and oranges. Well, you are right. Our point is that if you want to build massively concurrent systems, you should be using the tool that is best for the job, regardless of the underlying concurrency mechanism. As a result, the concurrency model of an Erlang program would differ from that of languages where process creation and message passing times are not as small. We describe the Erlang way of dealing with concurrency in Chapters 4 through 6, and Chapter 12.

Soft Real-Time Properties

Even though Erlang is a high-level language, you can use it for tasks with soft real-time constraints. Storage management in Erlang is automated, with garbage collection implemented on a per-process basis. This gives system response times on the order of milliseconds even in the presence of garbage-collected memory. Because of this, Erlang can handle high loads with no degradation in throughput, even during sustained peaks.

Robustness

How do you build a robust system? Although Erlang might not solve all your problems, it will greatly facilitate your task at a fraction of the effort of other programming languages. Thanks to a set of simple but powerful error-handling mechanisms and exception monitoring constructs, very general library modules have been built, with robustness designed into their core. By programming for the correct case and letting these libraries handle the errors, not only are programs shorter and easier to understand, but they will usually contain fewer bugs.

The libraries are collectively known as the OTP middleware. What exception monitoring and error-handling mechanisms do they contain, and what libraries are built on top of them?

  • Erlang processes can be linked together so that if one crashes, the other will be informed, and then can either handle the crash or choose to crash itself.

  • OTP provides a number of generic behaviors, such as servers, finite state machines, and event handlers. These worker processes have built-in robustness, since they handle all the general (and therefore difficult) concurrent parts of these patterns; all the user needs to do is to program the specific behavior of the particular server, which is much more straightforward to program than the general behavior.

  • These generic behaviors are linked to a supervisor behavior whose only task is to monitor and handle process termination. OTP puts the idea of links into a framework whereby a process supervises other workers and supervisors, and may itself be supervised by yet another process, all in a hierarchical structure. Figure 1-2 illustrates a typical supervision tree.

  • Using this supervision and linking, Erlang programmers can concentrate on programming for the correct case, and can let the process fail in any other circumstances. This avoidance of defensive programming makes a programmer’s task much easier, as well as making it more straightforward to understand how a program behaves.

Although in this book we concentrate on Erlang and its error-handling mechanisms and exception monitoring properties, we also provide an introduction to the OTP design patterns in Chapter 12.

An example supervision tree

Figure 1-2. An example supervision tree

Distributed Computation

Erlang has distribution incorporated into the language’s syntax and semantics, allowing systems to be built with location transparency in mind. The default distribution mode is based on TCP/IP, allowing a node (or Erlang runtime system) on a heterogeneous network to connect to any other node running on any operating system. As long as these nodes are connected through a TCP/IP network and the firewall has been correctly configured, the result is a fully meshed network of nodes, where all the nodes can communicate with each other.

As Erlang clusters were designed to execute behind firewalls, security is based on secret cookies with very few restrictions on access rights. You have the ability to create more disparate networks of distributed Erlang nodes using gateways, and if necessary, make them communicate using secure Internet protocols such as SSL.

Erlang programs consist of processes that communicate via message passing. When you start programming in Erlang, these will all be on one node, but as the syntax of sending a message within the node is the same as sending it to a remote node, you can easily distribute your processes across a cluster of computers. With distribution built into the language, operations such as clustering, load balancing, the addition of hardware and nodes, communication, and reliability come with very little overhead and correspondingly little code.

Integration and Openness

You want to use the right tool for the right job. Erlang is an open language allowing you to integrate legacy code or new code where programming languages other than Erlang are more suitable for the job. As a result, there are mechanisms for interworking with C, Java, Ruby, and other programming languages, including Python, Perl, and Lisp.

High-level libraries allow Erlang nodes to communicate with nodes executing Java or C, making them appear and behave like distributed Erlang nodes. Other external languages can be tied in more tightly using drivers that are linked into the Erlang runtime system itself, as a device driver would be, and sockets can also be used for communication between Erlang nodes and systems written in other languages using popular protocols such as HTTP, SNMP, and IIOP.

The fact that distribution is built into Erlang means that integrating it with other systems is more natural than in other languages. The facilities for handling network data formats are an important part of the language and its libraries, rather than a bolted-on afterthought. The tracing and logging facilities also give you a clear picture of how the integration is working, enabling you to debug and tune systems much more effectively.

The best content for your career. Discover unlimited learning on demand for around $1/day.