Navigation Menu

Skip to content

roblaing/erlang_mooc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Erlang Mooc Notes

This is space for my assignments and notes for two online courses on Erlang offered by University of Kent professor Simon Thompson, who is also co-author of O'Reilly's Erlang Programming book which the courses complement nicely.

  1. Functional Programming in Erlang
  2. Concurrent Programming in Erlang

Something I got converted to by an online course on open-source Lisp-dialect Racket using the free and excellent How to Design Programs (HTDP) textbook is its six step recipe which I've used to structure this README below.

Erlang has excellent tools for all this — probably more so than Racket which doesn't encourage modularisation much — but one criticism I have of Professor Thompson's course is while he touches on specifications, documentation, and testing, he doesn't give them the supremacy I feel they deserve.

To train myself in Erlang while developing the good habits of the HTDP school, I structured my subdirectories roughly according to the recommendations:

─ myproject
├── Makefile
├── doc
│   └── overview.edoc
├── ebin
├── priv
├── src
└── test

I've created my own small Makefile to keep things simpler than rebar3 or erlang.mk.

Running make will compile src/foo.erl into ebin/foo.beam.

Running make doc will use EDoc to create doc/index.html and related content which can be viewed by pointing your browser there.

Running make test will use Common Test to run test/foo_SUITE.erl files whose results you can view by pointing your browser to test/index.html.

Running erl -pa ebin lets you run the repl from the myproject root directory with the compiled code in the ebin subdirectory in its path.

Erlang falls very much into the HTDP school of encouraging abstraction.

1. From Problem Analysis to Data Definitions

Identify the information that must be represented and how it is represented in the chosen programming language. Formulate data definitions and illustrate them with examples.

The general advice for the first step is to step away from your computer and design with a pencil and paper. As someone more comfortable with a keyboard who can't read his own handwriting, I find working with graphviz, D3, or simply waffling away in a text editor (as I'm doing now) easier.

The key point is, the old cliché "The sooner you start to code, the longer the program will take" stands. As IBM liked to tell my dad via a paper calendar, "THINK".

2. Signature, Purpose Statement, Header

A reason the HTDP recipe bundles these three together is they need to be done concurrently. Since the guts of a program is likely to change often as you get more experienced with a given language — with your code hopefully getting shorter and faster as your knowledge grows — it's vital to focus on "what" rather than "how" first.

2.1 Signature

State what kind of data the desired function consumes and produces. -- HTDP

What HTDP terms signatures are also commonly known as APIs, contracts, or specifications, the jargon used in Erlang's -spec and -type annotations.

Type annotations provide good documentation of the program.

When you start writing a new module, think about types first and declare them before you write your code. Write type specifications for all the exported functions in your module. Do this before you start writing the code. -- Joe Armstrong

Here we think in terms of sets (the basic building blocks, ie atoms, of mathematics, as explained in a brilliant lesson by Eddie Woo), or types as sets are known in computer science, and operations on these sets.

Erlang promotes this thinking with its dialyzer and typer tools.

Tip: use typer foo.erl to check your -spec ... statements by commenting them out after you've written them. Regard it as a testing tool, not a substitute for thinking.

Try to tightly constrain the arguments to exported functions as much as possible. The more precise you can be about your types, the better results you will get with dialyzer. Using anonymous variables in arguments to a function often results in types that are far less specific than you had intended; try to constrain variables as much as possible. -- Joe Armstrong

As does any programming language, Erlang has a type hirarchy, a phrase I think Turing-award winner Barbara Liskov coined.

I've diagramed Erlang's type hirarchy (work in progress) like this:

any()
├── term()
│   ├── number()                   % union of integer() and float()
│   │   ├── integer()              % base#number eg 2:100 = 4   16#F09A29 = 15768105
│   │   │   ├── non_neg_integer()  % 0..
│   │   │   │   ├── pos_integer()  % 1..
│   │   │   │   ├── char()         % 33 (!) ... 255 (ÿ)
│   │   │   │   ├── byte()         % 0..255
│   │   │   │   └── arity()        % 0..255   bounded integer
│   │   │   └── neg_integer()      % ..-1
│   │   └── float()                % is_float(Term)
│   ├── atom()                     % is_atom(Term)
│   │   ├── boolean()              % false | true   bool() is a deprecated synonym
│   │   ├── module()
│   │   └── node()                 % eg 'dilbert@uab.ericsson.se' or dilbert@uab
│   ├── binary()                   % bit syntax
│   │   └── bitstring()

├── Compound │ ├── tuple() % {T1, T2, ..., TN} │ │ └── mta() % {module(), atom(), arity()} │ ├── list() % [any()] │ │ ├── nil() % [] │ │ ├── nonempty_list() % [T, ...] │ │ ├── maybe_improper_list(Type1, Type2) % eg [3|3] is an improper list │ │ ├── proplists:proplist() % [atom() | tuple()] │ │ └── string() % [char()] │ │ └── nonempty_string() │ ├── array:array() │ └── map()

├── Union │ ├── timeout() % non_neg_integer() | infinity

├── erlang:dst() │ ├── pid() % self(). │ ├── port() │ └── (RegName :: atom()) | {RegName :: atom(), Node :: node()} ├── reference() % erlang:make_ref(). ├── fun() % fun((...) -> Type) │ └── function() ├── ets:tid() └── none() └── no_return()

iodata()
iolist()

When it comes to the taxonomy of programming language, I find this joke checklist a handy reference. Erlang checks the box for dynamically-typed as opposed to statically-typed (the creators of the list ommited optionally-typed), which means it has lots of is_type(X) built-in functions (BIFs in Erlang jargon).

2.2 Purpose Statement

Formulate a concise answer to the question what the function computes.

In the case of Edoc, any comment like

%% @doc Purpose statement goes here...
myfunc(Arg1,...) ->
...

will then be included in the Function Index table and in the Function Details of the module's automatically generated html.

Besides being helpful to users, writing purpose statements also helps one think of suitable names for functions and their arguments to help make code self documenting.

2.3 Header

Define a stub that lives up to the signature.

The quip fake it till you make it is often used here. Without a stub to fool the compiler, we can't proceed with test-driven development.

All we want at this is stage is something with the same names and arities as our functions in our wish list whose return values are of the correct type (typically hard-coded return values to give usually a wrong answer).

Warnings about variables in the header that are not used in the body are fine, in fact a handy reminder this is just work in progress.

3. Functional Examples

Work through examples that illustrate the function’s purpose.

The first thing I tend to look for in documentation when learning an unfamiliar function is a simple example of how to use it.

Besides creating good documentation, example-driven development (a phrase I prefer to test-driven development since you need to think of examples before you can write tests) helps develop better final code.

One advantage of Erlang not being a top of the pops progamming language is it doesn't suffer as much from the too many tools problem, but there is nevertheless plenty of confusion.

I've used EUnit rather than Common Test in some cases since it's easier for small projects.

In an ideal world, the test and document tools would be integrated. Edoc isn't linked to any test framework I know of, so it's good practice to copy at least one illustrative example of each public function into its comment header for EDoc to put into the html.

4. Function Template

Translate the data definitions into an outline of the function.

Here we move from "what" to "how", exploring which architectural patterns fit the problem at hand.

Erlang's OTP design principles with their -behaviour(X) attribute encourages good practice here.

A nice thing about Erlang is it encourges top-down design of big systems rather than the myopia of bottom-up design.

5. Function Definition

Fill in the gaps in the function template. Exploit the purpose statement and the examples.

6. Testing

Articulate the examples as tests and ensure that the function passes all. Doing so discovers mistakes. Tests also supplement examples in that they help others read and understand the definition when the need arises—and it will arise for any serious program.

About

My assignments for the University of Kent's Erlang courses offered via Futurelearn

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published