diff --git a/_config.yml b/_config.yml index 18d7dd5ab..78201b070 100644 --- a/_config.yml +++ b/_config.yml @@ -1,3 +1,5 @@ markdown: redcarpet pygments: true permalink: /blog/:year/:month/:day/:title +redcarpet: + extensions: ['with_toc_data'] diff --git a/_includes/important-links.html b/_includes/important-links.html index ed9f35a05..69e53e5f0 100644 --- a/_includes/important-links.html +++ b/_includes/important-links.html @@ -25,7 +25,8 @@

Code editor support

  • Emacs Mode
  • Textmate Bundle
  • Vim Elixir
  • +
  • GtkSourceView (gedit)
  • -{% include sponsors.html %} \ No newline at end of file +{% include sponsors.html %} diff --git a/_includes/learning-resources.html b/_includes/learning-resources.html index 17e8d55f7..a30c82f1f 100644 --- a/_includes/learning-resources.html +++ b/_includes/learning-resources.html @@ -2,7 +2,7 @@

    Learning resources

    diff --git a/_includes/search.html b/_includes/search.html index 494a341ae..3bd4fe69d 100644 --- a/_includes/search.html +++ b/_includes/search.html @@ -1,6 +1,6 @@

    - News: Elixir v0.11.0 released + News: Elixir v0.12.0 released

    diff --git a/_layouts/getting_started.html b/_layouts/getting_started.html index 11647d40a..cfcb93ce3 100644 --- a/_layouts/getting_started.html +++ b/_layouts/getting_started.html @@ -33,6 +33,12 @@

    ExUnit - a unit test framework

  • Introduction to ExUnit
  • +
    +

    Other

    +
      +
    1. Erlang/Elixir Syntax: A Crash Course
    2. +
    +
    {% include learning-resources.html %} {% include sponsors.html %} diff --git a/_posts/2013-08-08-elixir-design-goals.markdown b/_posts/2013-08-08-elixir-design-goals.markdown index d69e5a38b..f62442ac8 100644 --- a/_posts/2013-08-08-elixir-design-goals.markdown +++ b/_posts/2013-08-08-elixir-design-goals.markdown @@ -88,7 +88,7 @@ This simple example illustrates how a developer can leverage macros to provide a Those examples are just scratching the surface of what can be achieved with macros in Elixir. For example, we are currently using macros to compile routes from a web application into a bunch of patterns that are highly optimizable by the VM, providing an expressive but heavily optimized routing algorithm. -The macro system also caused a huge imapct on the syntax, which we will discuss briefly before moving to the last goal. +The macro system also caused a huge impact on the syntax, which we will discuss briefly before moving to the last goal. ### Syntax diff --git a/_posts/2013-12-11-elixir-s-new-continuable-enumerators.markdown b/_posts/2013-12-11-elixir-s-new-continuable-enumerators.markdown new file mode 100644 index 000000000..970004c39 --- /dev/null +++ b/_posts/2013-12-11-elixir-s-new-continuable-enumerators.markdown @@ -0,0 +1,213 @@ +--- +layout: post +title: Elixir's new continuable enumerators +author: Peter Minten +category: "What's New in Elixir" +excerpt: In 0.12.0 Elixir's enumerators have gained the ability to suspend value + production and to terminate early. +--- + +As you may have heard in the upcoming 0.12.0 release Elixir's enumerators gained +some new features. In this blog post I'll explain what's new, what it enables +and how it works. + +For those of you who use the development version of Elixir these changes are +already available. For the exact differences in code you can look at the +[relevant pull request](https://github.com/elixir-lang/elixir/pull/1922). + +## A recap of enumerators, and some terminology + +The basic idea of enumerators is that you traverse some data structure or +resource (lines from a file) by putting the thing that is traversed in control. +That is if you're reading from a file you have a loop that reads lines from a +file and for each line calls a function. Just calling a function isn't all that +useful for most tasks as there'd be no way to remember previous lines (ugly +hacks aside), so some accumulator value is passed to the function and a new +accumulator is returned by it. + +For example here's how you can count the total length of strings in a list. + +```elixir +Enumerable.reduce(l, 0, fn x, acc -> String.length(x) + acc end) +``` + +Often the actual call to `Enumerable.reduce/3` is hidden inside another +function. Say that we want to define a `sum` function. The usual way is to +write it like this: + +```elixir +def sum(coll) do + Enumerable.reduce(coll, 0, fn x, acc -> x + acc end) +end +``` + +This could get called as `Enum.map(1..10, &(&1 * &1)) |> sum()` to get the sum of +squares. Desugaring this means `sum(Enum.map(1..10, &(&1 * &1)))`. + +The general pattern is this: + +```elixir +def outer_function(coll, ...) do + ... + Enumerable.reduce(coll, initial_consumer_acc, consumer) + ... +end + +something_that_returns_an_enumerable(...) |> outer_function(...) +``` + +You'll notice the slightly uncommon terminology of "outer function" and +"consumer" (normally called an "iteratee"). That's intentional, naming an +iteratee a consumer better reflects that it consumes values. + +Along the same lines I call the reduce function for a specific enumerable a +producer, it produces values which are given to a consumer. + +The outer function is the function to which the enumerable is passed. +Syntactically it looks like this is the consumer, but it's really a function +that combines the producer and the consumer. For simple consumers (say `fn x, +acc -> length(x) + acc end`) the consumer will often be written directly in the +source text of the outer function, but let's try to keep those concepts +distinguished. + +## Two issues with classic Elixir enumerators + +Enumerators are great, but they have their limitations. One issue is that it's +not possible to define a function that only returns at most 3 elements without +traversing all elements or using ugly tricks such as `throw` (with a +`try...catch` construct in the outer function). The `throw` trick is used in +`Enum` and `Stream` to implement functions such as `Enum.take/2` and +`Stream.take_while/2`. It works, but it's not what I'd call stylish. + +A bigger problem, that doesn't have a workaround, is that there's no way to +interleave two enumerables. That is, it's not possible to define a function that +for two enumerables `A` and `B` returns a list `[A1, B1, A2, B2, A3, ...]` +(where `A1` is the first element of A) without first traversing both lists and +then interleaving the collected values. Interleaving is important because it's +the basis of a zip function. Without interleaving you cannot implement +`Stream.zip/2`. + +The underlying problem, in both cases, is that the producer is fully in control. +The producer simply pushes out as many elements to the consumer as it wants and +then says "I'm done". There's no way aside from `throw`/`raise` for a consumer +to tell a producer "stop producing". There is definitely no way to tell a +producer "stop for now but be prepared to continue where you left off later". + +## Power to the consumer! + +At CodeMeshIO José Valim and Jessica Kerr sat down and discussed this problem. +They came up with a solution inspired by a [Monad.Reader +article](http://themonadreader.files.wordpress.com/2010/05/issue16.pdf) (third +article). It's an elegant extension of the old system, based on a simple idea. +Instead of returning only an accumulator at every step (for every produced +value) the consumer returns a combination of an accumulator and an instruction +to the producer. Three instructions are available: + +* `:cont` - Keep producing. +* `:halt` - Stop producing. +* `:suspend` - Temporarily stop producing. + +A consumer that always returns `:cont` makes the producer behave exactly the +same as in the old system. A consumer may return `:halt` to have the producer +terminate earlier than it normally would. + +The real magic is in `:suspend` though. It tells a producer to return the +accumulator and a continuation function. + +```elixir +{ :suspended, n_, cont } = Enumerable.reduce(1..5, { :cont, 0 }, fn x, n -> + if x == 3 do + { :suspend, n } + else + { :cont, n + x } + end +end) +``` + +After running this code `n_` will be `3` (1 + 2) and `cont` will be a +function. We'll get back to `cont` in a minute but first take a look at some of +the new elements here. The initial accumulator has an instruction as well, so +you could suspend or halt a producer immediately, if you really want to. The +value passed to the consumer (`n`) does not contain the instruction. The return +value of the producer also has a symbol in it. Like with the instructions of +consumers there are three possible values: + +* `:done` - Completed normally. +* `:halted` - Consumer returned a `:halt` instruction. +* `:suspended` - Consumer return a `:suspend` instruction. + +Together with the other values returned the possible return values from a +producer are `{ :done, acc } | { :halted, acc } | { :suspended, acc, +continuation }`. + +Back to the continuation. A continuation is a function that given an accumulator +returns a new producer result. In other words it's a way to swap out the +accumulator but keep the same producer in the same state. + +## Implementing `interleave` + +Using the power of suspension it is now possible to create an interleave +function. + +```elixir +defmodule Interleave do + def interleave(a, b) do + step = fn x, acc -> { :suspend, [x|acc] } end + af = &Enumerable.reduce(a, &1, step) + bf = &Enumerable.reduce(b, &1, step) + do_interleave(af, bf, []) |> :lists.reverse() + end + + defp do_interleave(a, b, acc) do + case a.({ :cont, acc }) do + { :suspended, acc, a } -> + case b.({ :cont, acc }) do + { :suspended, acc, b } -> + do_interleave(a, b, acc) + { :halted, acc } -> + acc + { :done, acc } -> + finish_interleave(a, acc) + end + { :halted, acc } -> + acc + { :done, acc } -> + finish_interleave(b, acc) + end + end + + defp finish_interleave(a_or_b, acc) do + case a_or_b.({ :cont, acc }) do + { :suspended, acc, a_or_b } -> + finish_interleave(a_or_b, acc) + { _, acc } -> + acc + end + end +end + +Interleave.interleave([1,2], [:a, :b, :c, :d]) +#=> [1, :a, 2, :b, :c, :d] +``` + +Lets go through this step by step. The main `interleave` function first +partially applies `Enumerable.reduce/3` to get function values that work just +like the continuations. This makes things easier for `do_interleave`. + +The `do_interleave` function first calls `a` (`af` from `interleave`) with the +`step` function so that the available element of `a` gets added to the +accumulator and `a` immediately suspends afterwards. Then the same is done for +`b`. If either producer is done all the remaining elements of the other get +added to the accumulator list. + +Note that `acc` is sometimes used to mean a tuple like `{ :cont, x }` and +sometimes the accumulator value proper. It's a bit confusing, yes. + +This example shows that through clever combination of an outer function +(`do_interleave`) and an inner function `step` two producers can be interleaved. + +## Conclusion + +The new system of enumerators certainly makes things a bit more complicated but +also adds power. I suspect many interesting and "interesting" functions can be +built on top of it. diff --git a/_posts/2013-12-15-elixir-v0-12-0-released.markdown b/_posts/2013-12-15-elixir-v0-12-0-released.markdown new file mode 100644 index 000000000..200a4a5cf --- /dev/null +++ b/_posts/2013-12-15-elixir-v0-12-0-released.markdown @@ -0,0 +1,52 @@ +--- +layout: post +title: Elixir v0.12.0 released +author: José Valim +category: Releases +excerpt: Elixir v0.12.0 is out with improved enumerables, build patterns and welcoming a new member to our team +--- + +Elixir v0.12.0 has been released with improved enumerables, build patterns and welcoming a new member to our team. + +## Enumerables + +In previous versions, the Enumerable protocol was based on reduce/fold, and while it is very efficient for operations like `map`, `reduce` and `filter`, it was sub-optimal for operations that need to halt, like `take` and `take_while`, and it made it impossible for operations like `zip` to be implemented. + +In v0.12.0, Elixir's Enumerable protocol has been extended to allow suspension and halting mechanisms, making operations like `take` simpler and operations that require interleaving, like `zip`, possible. + +Although most users don't need to concern with the implementation of the Enumerable protocol, the side-effect is that both `Enum` and `Stream` modules have been considerably extended and improved in this release, with more than 15 new functions added to the `Stream` module. + +## Mix + +The tool that received most improvements in this release was Mix. The biggest change is that Mix no longer compiles projects in place but to the `_build` directory. For example, take the [Ecto project](https://github.com/elixir-lang/ecto) that [depends on `postgrex` and `poolboy`](https://github.com/elixir-lang/ecto/blob/master/mix.exs#L24-L25). When compiled, all the artifacts will be placed in the `_build` directory like this: + +``` +_build +└── shared + └── lib + ├── ecto + │   └── ebin + | └── priv + ├── poolboy + │   └── ebin + └── postgrex + └── ebin +``` + +For those familiar with Erlang's OTP, this is similar to the structure used by OTP when releasing your software. So this new structure makes our software one step close to production and guarantee it is designed correctly since day 1. + +This approach comes with the `:build_per_environment` option which, when set to true, creates a different build per environment (`dev`, `test`, `production` or more). Extremely useful when a project compile different artifacts depending on the compilation environment. + +Mix has also added support to optional dependencies and improved common patterns, like the usage of umbrella apps. + +## Welcome, Eric! + +With this release, we also want to welcome [Eric MJ](https://github.com/ericmj) to the Elixir Team. He has done fantastic work on Elixir, helping us maintain the codebase and working on many of the important features from previous releases and now many more to come. + +Eric is also maintainer of both [Ecto](https://github.com/elixir-lang/ecto) and [Postgrex](https://github.com/ericmj/postgrex) projects. Which are proving to be very useful to the Elixir community too! + +## Tidying up + +There were other small changes, like additions to the `Float` module and improvements the to the typespec syntax. To see the full list, please [see the CHANGELOG](https://github.com/elixir-lang/elixir/blob/v0.12.0/CHANGELOG.md). + +Give Elixir a try! You can start with our [getting started guide](http://elixir-lang.org/getting_started/1.html), or check out our sidebar for other learning resources. diff --git a/_posts/2013-12-27-parsing-xml-with-elixir.markdown b/_posts/2013-12-27-parsing-xml-with-elixir.markdown new file mode 100644 index 000000000..c73d63867 --- /dev/null +++ b/_posts/2013-12-27-parsing-xml-with-elixir.markdown @@ -0,0 +1,85 @@ +--- +layout: post +title: Parsing XML With Elixir +author: Josh Adams +category: examples +excerpt: An example of interoperating with Erlang's built-in `xmerl` library to parse XML. +--- + +One of the latest episodes of [ElixirSips](http://elixirsips.com/episodes/028_parsing_xml.html) +provided an example of parsing XML via the built in `xmerl` library in Erlang: + + + +Parsing XML is a common need to different applications. The example also show cases +how to integrate with existing Erlang libraries. You can watch the video for a complete +rundown where we use tests to explore the `xmerl` library and how we can use it from +Elixir. If you just want to see the results, here's the test file we ended up with, +commented for clarity: + +```elixir +# If you want to pattern-match on a record defined in an erlang library, you +# need to use Record.extract to turn it into an Elixir record data structure. +# Here, we extract xmlElement and xmlText from xmerl. +defrecord :xmlElement, Record.extract(:xmlElement, from_lib: "xmerl/include/xmerl.hrl") +defrecord :xmlText, Record.extract(:xmlText, from_lib: "xmerl/include/xmerl.hrl") + +defmodule XmlParsingTest do + use ExUnit.Case + + # Here we define some simple XML that we'll work with in our tests. + def sample_xml do + """ + + + XML Parsing + + +

    Neato

    + + + + """ + end + + test "parsing the title out" do + { xml, _rest } = :xmerl_scan.string(bitstring_to_list(sample_xml)) + [ title_element ] = :xmerl_xpath.string('/html/head/title', xml) + [ title_text ] = title_element.content + title = title_text.value + + assert title == 'XML Parsing' + end + + test "parsing the p tag" do + { xml, _rest } = :xmerl_scan.string(bitstring_to_list(sample_xml)) + [ p_text ] = :xmerl_xpath.string('/html/body/p/text()', xml) + + assert p_text.value == 'Neato' + end + + test "parsing the li tags and mapping them" do + { xml, _rest } = :xmerl_scan.string(bitstring_to_list(sample_xml)) + li_texts = :xmerl_xpath.string('/html/body/ul/li/text()', xml) + texts = li_texts |> Enum.map(fn(x) -> x.value end) + + assert texts == ['First', 'Second'] + end +end +``` + +The project built in the episode is also +[available for download](http://elixirsips.com/downloads/028_parsing_xml.tar.gz). + +## Resources + +- [xmerl user guide](http://www.erlang.org/doc/apps/xmerl/xmerl_ug.html) +- [xmerl manual](http://www.erlang.org/doc/man/xmerl_scan.html) +- [erlsom](https://github.com/willemdj/erlsom) +- [exml](https://github.com/paulgray/exml) +- [Differences between Erlang and Elixir records](http://elixir-lang.org/crash-course.html#notable_differences) - See the 'Records' section. +- [Dave Thomas on parsing XML in Erlang](http://pragdave.pragprog.com/pragdave/2007/04/a_first_erlang_.html) +- [`xmlElement` record](https://github.com/erlang/otp/blob/maint/lib/xmerl/include/xmerl.hrl#L73-L85) diff --git a/crash-course.markdown b/crash-course.markdown index 495a16a4b..cbe62662b 100644 --- a/crash-course.markdown +++ b/crash-course.markdown @@ -1,9 +1,10 @@ --- +title: "Erlang/Elixir Syntax: A Crash Course" section: home layout: default --- -# Erlang/Elixir Syntax: A Crash Course +# {{ page.title }} This is a quick introduction to the Elixir syntax for Erlang developers and vice-versa. It is the absolute minimum amount of knowledge you need in order to understand Elixir/Erlang code, support interoperability, read the docs, sample code, etc. @@ -101,7 +102,6 @@ Some operators are spelled differently. | =/= | !== | A negative match | | /= | != | Not equals | | =< | <= | Less than or equals | - | ! | <- | Send messages | ### Delimiters @@ -261,7 +261,7 @@ setelement(1, { a, b, c }, d) %=> { d, b, c } ```elixir elem({ :a, :b, :c }, 0) #=> :a -setelem({ :a, :b, :c }, 0, :d) #=> { :d, :b, :c } +set_elem({ :a, :b, :c }, 0, :d) #=> { :d, :b, :c } ``` ### Lists and Binaries @@ -313,14 +313,14 @@ re:run("abc ", Pattern). **Elixir** ```elixir -Regex.run %r/abc\s/, "abc " +Regex.run ~r/abc\s/, "abc " #=> ["abc "] ``` Regexes are also supported in heredocs, which is convenient when defining multiline regexes: ```elixir -is_regex %r""" +is_regex ~r""" This is a regex spawning several lines. @@ -376,14 +376,14 @@ Each Erlang module lives in its own file which has the following structure: ```erlang -module(hello_module). --export([some_fun/0, fun/1]). +-export([some_fun/0, some_fun/1]). % A "Hello world" function some_fun() -> io:format('~s~n', ['Hello world!']). % This one works only with lists -fun(List) when is_list(List) -> +some_fun(List) when is_list(List) -> io:format('~s~n', List). % Non-exported functions are private @@ -566,7 +566,7 @@ sum "a", "b" In addition, Elixir allows for default values for arguments, whereas Erlang does not. ```elixir -def mul_by(x, n // 2) do +def mul_by(x, n \\ 2) do x * n end @@ -815,7 +815,7 @@ end. ```elixir pid = Kernel.self -pid <- { :hello } +send pid, { :hello } receive do { :hello } -> :ok diff --git a/css/style.css b/css/style.css index a499b6dd0..b5013362f 100644 --- a/css/style.css +++ b/css/style.css @@ -42,16 +42,6 @@ body { #container { background-color: #FFF; } -::-moz-selection { - background: #000; - color: #fff; - text-shadow: none; -} -::selection { - background: #000; - color: #fff; - text-shadow: none; -} a, a:visited { color: #4E2A8E; text-decoration: underline; @@ -578,6 +568,7 @@ body.source div.menu li.source a { height: 150px; margin: 3px 25px 20px 0; } +.no-border { border: 0 }; /* Post titles -------------------------------------------------------------- */ diff --git a/docs.html b/docs.html index 4c6afd2e5..73716c250 100644 --- a/docs.html +++ b/docs.html @@ -1,4 +1,5 @@ --- +title: Elixir Documentation section: docs layout: default --- diff --git a/getting_started/1.markdown b/getting_started/1.markdown index f4e711fca..f352e1ba3 100644 --- a/getting_started/1.markdown +++ b/getting_started/1.markdown @@ -17,9 +17,9 @@ Keep in mind that Elixir is still in development, so if at any point you receive ## 1.1 Installation -The only prerequisite for Elixir is Erlang, version R16B or later. You can find the source code for [Erlang here](http://www.erlang.org/download.html) or use one of the [precompiled packages](https://www.erlang-solutions.com/downloads/download-erlang-otp). +The only prerequisite for Elixir is Erlang, version R16B or later, which can be easily installed with [Precompiled packages](https://www.erlang-solutions.com/downloads/download-erlang-otp). In case you want to install it directly from source, it can be found on [the Erlang website](http://www.erlang.org/download.html) or by following the excellent tutorial available in the [Riak documentation](http://docs.basho.com/riak/1.3.0/tutorials/installation/Installing-Erlang/). -For Windows developers, we recommend the precompiled package. Those on a UNIX platform can probably get Erlang installed via one of the many package management tools. +For Windows developers, we recommend the precompiled packages. Those on a UNIX platform can probably get Erlang installed via one of the many package management tools. After Erlang is installed, you should be able to open up the command line (or command prompt) and check the Erlang version by typing `erl`. You will see some information as follows: @@ -31,7 +31,7 @@ After Erlang is up and running, it is time to install Elixir. You can do that vi ### 1.1.1 Distributions -This tutorial requires Elixir v0.10.2 or later and it may be available in some distributions: +This tutorial requires Elixir v0.12.4 or later and it may be available in some distributions: * Homebrew for Mac OS X * Update your homebrew to latest with `brew update` @@ -50,9 +50,13 @@ This tutorial requires Elixir v0.10.2 or later and it may be available in some d If you don't use any of the distributions above, don't worry, we also provide a precompiled package! -### 1.1.2 Compiling from source (Unix and MinGW) +### 1.1.2 Precompiled package -You can download and compile Elixir in few steps. You can get the [latest stable release here](https://github.com/elixir-lang/elixir/tags), unpack it and then run `make` inside the unpacked directory. After that, you are ready to run the `elixir` and `iex` commands from the `bin` directory. It is recommended that you add Elixir's `bin` path to your PATH environment variable to ease development: +Elixir provides a [precompiled package for every release](https://github.com/elixir-lang/elixir/releases/). After downloading and unzip-ing the package, you are ready to run the `elixir` and `iex` commands from the `bin` directory. It is recommended that you also add Elixir's `bin` path to your PATH environment variable to ease development. + +### 1.1.3 Compiling from source (Unix and MinGW) + +You can download and compile Elixir in few steps. You can get the [latest stable release here](https://github.com/elixir-lang/elixir/releases/), unpack it and then run `make` inside the unpacked directory. After that, you are ready to run the `elixir` and `iex` commands from the `bin` directory. It is recommended that you add Elixir's `bin` path to your PATH environment variable to ease development: $ export PATH="$PATH:/path/to/elixir/bin" @@ -64,12 +68,6 @@ In case you are feeling a bit more adventurous, you can also compile from master If the tests pass, you are ready to go. Otherwise, feel free to open an issue [in the issues tracker on Github](https://github.com/elixir-lang/elixir). -### 1.1.3 Precompiled package - -Elixir provides a [precompiled package for every release](https://github.com/elixir-lang/elixir/releases/). Precompiled packages are the best option if you are developing on Windows. - -After downloading and unzip-ing the package, you are ready to run the `elixir` and `iex` commands from the `bin` directory. It is recommended that you also add Elixir's `bin` path to your PATH environment variable to ease development. - ## 1.2 Interactive mode When you install Elixir, you will have three new executables: `iex`, `elixir` and `elixirc`. If you compiled Elixir from source or are using a packaged version, you can find these inside the `bin` directory. diff --git a/getting_started/2.markdown b/getting_started/2.markdown index fe02223aa..67175adbb 100644 --- a/getting_started/2.markdown +++ b/getting_started/2.markdown @@ -508,11 +508,11 @@ x = 1 x #=> 1 ``` -### 2.6.4 Receive +### 2.6.4 Receive This next control-flow mechanism is essential to Elixir's actors. In Elixir, the code is run in separate processes that exchange messages between them. Those processes are not Operating System processes (they are actually quite light-weight) but are called so since they do not share state with each other. -In order to exchange messages, each process has a mailbox where the received messages are stored. The `receive` mechanism allows us to go through this mailbox searching for a message that matches the given pattern. Here is an example that uses the arrow operator `<-` to send a message to the current process and then collects this message from its mailbox: +In order to exchange messages, each process has a mailbox where the received messages are stored. The `receive` mechanism allows us to go through this mailbox searching for a message that matches the given pattern. Here is an example that uses the `send/2` function to send a message to the current process and then collects this message from its mailbox: ```iex # Get the current process id @@ -520,7 +520,7 @@ iex> current_pid = self # Spawn another process that will send a message to current_pid iex> spawn fn -> - current_pid <- { :hello, self } + send current_pid, { :hello, self } end <0.36.0> diff --git a/getting_started/3.markdown b/getting_started/3.markdown index 8e297cb2c..85ecaeb58 100644 --- a/getting_started/3.markdown +++ b/getting_started/3.markdown @@ -75,8 +75,7 @@ defmodule Math do end end -# IO.puts expects a list or a binary, so we use the string representation of the sum -IO.puts to_string(Math.sum(1, 2)) +IO.puts Math.sum(1, 2) ``` And execute it as: @@ -130,7 +129,7 @@ Named functions also support default arguments: ```elixir defmodule Concat do - def join(a, b, sep // " ") do + def join(a, b, sep \\ " ") do a <> sep <> b end end @@ -143,7 +142,7 @@ Any expression is allowed to serve as a default value, but it won't be evaluated ```elixir defmodule DefaultTest do - def dowork(x // IO.puts "hello") do + def dowork(x \\ IO.puts "hello") do x end end @@ -161,7 +160,7 @@ If a function with default values has multiple clauses, it is recommended to cre ```elixir defmodule Concat do - def join(a, b // nil, sep // " ") + def join(a, b \\ nil, sep \\ " ") def join(a, b, _sep) when nil?(b) do a @@ -186,7 +185,7 @@ defmodule Concat do a <> b end - def join(a, b, sep // " ") do + def join(a, b, sep \\ " ") do IO.puts "***Second join" a <> sep <> b end @@ -464,6 +463,6 @@ In other words, we are simply calling the function `flatten` on the atom `:lists ```iex iex> is_atom(List) true -iex> to_binary(List) +iex> to_string(List) "Elixir.List" ``` diff --git a/getting_started/4.markdown b/getting_started/4.markdown index 95cd7d42a..ba5cc69eb 100644 --- a/getting_started/4.markdown +++ b/getting_started/4.markdown @@ -87,7 +87,7 @@ For more information on records, [check out the documentation for the `defrecord ## 4.2 Protocols -Protocols are a mechanism to achieve polymorphism in Elixir. Dispatching a protocol is available to any data type as long as it implements the protocol. Let's consider a practical example. +Protocols are a mechanism to achieve polymorphism in Elixir. Dispatching on a protocol is available to any data type as long as it implements the protocol. Lets consider a practical example. In Elixir, only `false` and `nil` are treated as false. Everything else evaluates to true. Depending on the application, it may be important to specify a `blank?` protocol that returns a boolean for other data types that should be considered blank. For instance, an empty list or an empty binary could be considered blanks. @@ -174,7 +174,7 @@ Now all data types that we have not implemented the `Blank` protocol for will be ### 4.2.2 Using protocols with records -The power of Elixir extensibility comes when protocols and records are mixed. +The power of Elixir's extensibility comes when protocols and records are mixed. For instance, Elixir provides a `HashDict` implementation that is an efficient data structure to store many keys. Let's take a look at how it works: @@ -188,7 +188,7 @@ If we inspect our `HashDict`, we can see it is a simple tuple: ```elixir inspect(dict, raw: true) -#=> { HashDict, 1, [{:hello,"world"}] } +#=> "{HashDict, 1, [[:hello | \"world\"]]}" ``` Since `HashDict` is a data structure that contains values, it would be convenient to implement the `Blank` protocol for it too: @@ -207,13 +207,7 @@ Blank.blank?(dict) #=> false Blank.blank?(HashDict.new) #=> true ``` -Excellent! The best of all is that we implemented the `Blank` protocol for an existing data structure (`HashDict`) without a need to wrap it or recompile it, which allows developers to easily extend previously defined protocols. Note this only worked because, when we defined the protocol, we have added `Record` to the list of types supported by the protocol: - -```elixir -@only [Atom, Record, Tuple, List, BitString, Any] -``` - -Keep in mind that `Record` needs to come before `Tuple`, since all records are tuples (but not all tuples are records). For this reason, in case a record does not implement a given protocol, Elixir will fall back to the tuple implementation of that protocol if one exists. So one can add a default protocol implementation for all records by simply defining a default implementation for tuples. +Excellent! The best of all is that we implemented the `Blank` protocol for an existing data structure (`HashDict`) without a need to wrap it or recompile it, which allows developers to easily extend previously defined protocols. ### 4.2.3 Built-in protocols @@ -239,7 +233,7 @@ iex> Enum.reduce 1..3, 0, fn(x, acc) -> x + acc end 6 ``` -* `Binary.Inspect` - this protocol is used to transform any data structure into a readable textual representation. This is what tools like IEx use to print results: +* `Inspect` - this protocol is used to transform any data structure into a readable textual representation. This is what tools like IEx use to print results: ```iex iex> { 1, 2, 3 } @@ -248,7 +242,7 @@ iex> HashDict.new #HashDict<[]> ``` - Keep in mind that whenever the inspected value starts with `#`, it is representing a data structure in non-valid Elixir syntax. For those, the true representation can be retrieved by calling `inspect` directly and passing `raw` as an option: + Keep in mind that, by convention, whenever the inspected value starts with `#`, it is representing a data structure in non-valid Elixir syntax. For those, the true representation can be retrieved by calling `inspect` directly and passing `raw` as an option: ```iex iex> inspect HashDict.new, raw: true diff --git a/getting_started/5.markdown b/getting_started/5.markdown index b72deeba5..7c07af3e0 100644 --- a/getting_started/5.markdown +++ b/getting_started/5.markdown @@ -28,7 +28,7 @@ Operators are also represented as such tuples: ```iex iex> quote do: 1 + 2 -{ :+, [], [1, 2] } +{:+, [context: Elixir, import: Kernel], [1, 2]} ``` Even a tuple is represented as a call to `{}`: @@ -49,7 +49,7 @@ When quoting more complex expressions, we can see the representation is composed ```iex iex> quote do: sum(1, 2 + 3, 4) -{ :sum, [], [1, { :+, [], [2, 3] }, 4] } +{:sum, [], [1, {:+, [context: Elixir, import: Kernel], [2, 3]}, 4]} ``` In general, each node (tuple) above follows the following format: diff --git a/getting_started/6.markdown b/getting_started/6.markdown index 6f047f4f2..d2525fef0 100644 --- a/getting_started/6.markdown +++ b/getting_started/6.markdown @@ -11,27 +11,27 @@ This chapter contains different small topics that are part of Elixir's day to da ## 6.1 String sigils -Elixir provides string sigils via the token `%`: +Elixir provides string sigils via the token `~`: ```elixir -%s(String with escape codes \x26 interpolation) -%S(String without escape codes and without #{interpolation}) +~s(String with escape codes \x26 interpolation) +~S(String without escape codes and without #{interpolation}) ``` Sigils starting with an uppercase letter never escape characters or do interpolation. Notice the separators are not necessarily parenthesis, but any non-alphanumeric character: ```elixir -%s-another string- +~s-another string- ``` -Internally, `%s` is translated as a call to `sigil_s`. For instance, the docs for `%s` are available in the macro [`sigil_s/2`](/docs/stable/Kernel.html#sigil_s/2) defined in `Kernel` module. +Internally, `~s` is translated as a call to `sigil_s`. For instance, the docs for `~s` are available in the macro [`sigil_s/2`](/docs/stable/Kernel.html#sigil_s/2) defined in the `Kernel` module. The sigils defined in Elixir by default are: -* `%c` and `%C` - Returns a char list; -* `%r` and `%R` - Returns a regular expression; -* `%s` and `%S` - Returns a string; -* `%w` and `%W` - Returns a list of "words" split by whitespace; +* `~c` and `~C` - Returns a char list; +* `~r` and `~R` - Returns a regular expression; +* `~s` and `~S` - Returns a string; +* `~w` and `~W` - Returns a list of "words" split by whitespace; ## 6.2 Heredocs @@ -58,7 +58,7 @@ String heredocs in Elixir use """ Notice the sigils discussed in the previous section are also available as heredocs: ```elixir -%S""" +~S""" A heredoc without escaping or interpolation """ ``` @@ -124,7 +124,7 @@ As you can see, invoking `h()` prints the documentation of `IEx.Helpers`. From t ```iex iex> h(c/2) -* def c(files, path // ".") +* def c(files, path \\ ".") ... :ok ``` @@ -177,13 +177,13 @@ iex> fun.("hello") A capture also allows the captured functions to be partially applied, for example: ```iex -iex> fun = &atom_to_binary(&1, :utf8) -#Function<6.17052888 in :erl_eval.expr/5> -iex> fun.(:hello) -"hello" +iex> fun = &rem(&1, 2) +#Function<6.80484245 in :erl_eval.expr/5> +iex> fun.(4) +0 ``` -In the example above, we use `&1` as a placeholder, generating a function with one argument. The capture above is equivalent to `fn(x) -> atom_to_binary(x, :utf8) end`. +In the example above, we use `&1` as a placeholder, generating a function with one argument. The capture above is equivalent to `fn(x) -> rem(x, 2) end`. Since operators are treated as regular function calls in Elixir, they are also supported, although they require explicit parentheses: @@ -287,7 +287,6 @@ iex> bc <> inbits " hello world ", c != ?\s, do: <> Elixir provides a set of pseudo-variables. These variables can only be read and never assigned to. They are: * `__MODULE__` - Returns an atom representing the current module or nil; -* `__FILE__` - Returns a string representing the current file; * `__DIR__` - Returns a string representing the current directory; * `__ENV__` - Returns a [Macro.Env](/docs/stable/Macro.Env.html) record with information about the compilation environment. Here we can access the current module, function, line, file and others; * `__CALLER__` - Also returns a [Macro.Env](/docs/stable/Macro.Env.html) record but with information of the calling site. `__CALLER__` is available only inside macros; diff --git a/getting_started/mix/1.markdown b/getting_started/mix/1.markdown index dcf4fb6fd..5ff834d59 100644 --- a/getting_started/mix/1.markdown +++ b/getting_started/mix/1.markdown @@ -63,7 +63,7 @@ defmodule MyProject.Mixfile do # { :foobar, git: "https://github.com/elixir-lang/foobar.git", tag: "0.1" } # # To specify particular versions, regardless of the tag, do: - # { :barbat, "~> 0.1", github: "elixir-lang/barbat.git" } + # { :barbat, "~> 0.1", github: "elixir-lang/barbat" } defp deps do [] end @@ -235,7 +235,7 @@ Use `mix help` to get more information. ### 1.4.5 Dependencies of dependencies -If your dependency is another Mix or rebar project, Mix does the right thing: it will automatically fetch and handle all dependencies of your dependencies. However, if your project have two dependencies that share the same dependency and the SCM information for the shared dependency doesn't match between the parent dependencies, Mix will mark that dependency as diverged and emit a warning. To solve this issue you can declare the shared dependency in your project and Mix will use that SCM information to fetch the dependency. +If your dependency is another Mix or rebar project, Mix does the right thing: it will automatically fetch and handle all dependencies of your dependencies. However, if your project has two dependencies that share the same dependency and the SCM information for the shared dependency doesn't match between the parent dependencies, Mix will mark that dependency as diverged and emit a warning. To solve this issue you can declare the shared dependency in your project with the option `override: true` and Mix will use that SCM information to fetch the dependency. ## 1.5 Umbrella projects diff --git a/getting_started/mix/2.markdown b/getting_started/mix/2.markdown index 0fbbce308..6976cba19 100644 --- a/getting_started/mix/2.markdown +++ b/getting_started/mix/2.markdown @@ -9,7 +9,7 @@ total_guides: 3 Where do we keep state in Elixir? -Our software needs to keep state, configuration values, data about the running system, etc. We have learned in previous sections how we can use processes/actors to keep state, receiving and responding to messages in a loop but this approach seems to be brittle. What happens if there is an error in our actor and it crashes? Even more, is it really required to create a new process when all we want to do is to keep simple configuration values? +Our software needs to keep state, configuration values, data about the running system, etc. We have learned in [previous sections](../2.html#receive) how we can use processes/actors to keep state, receiving and responding to messages in a loop but this approach seems to be brittle. What happens if there is an error in our actor and it crashes? Even more, is it really required to create a new process when all we want to do is to keep simple configuration values? In this chapter, we will answer those questions by building an OTP application. In practice, we don't need Mix in order to build such applications, however Mix provides some conveniences that we are going to explore throughout this chapter. diff --git a/index.html b/index.html index 40a89d256..439cbb969 100644 --- a/index.html +++ b/index.html @@ -4,19 +4,17 @@ ---
    -
    +
    Elixir Sample

    Elixir is a functional, meta-programming aware language built on top of the Erlang VM. It is a dynamic language with flexible syntax and macro support that leverages Erlang's abilities to build concurrent, distributed and fault-tolerant applications with hot code upgrades.

    -

    Elixir also provides first-class support for pattern matching, polymorphism via protocols (similar to Clojure's), aliases and associative data structures (usually known as dicts or hashes in other programming languages).

    -

    Finally, Elixir and Erlang share the same bytecode and data types. This means you can invoke Erlang code from Elixir (and vice-versa) without any conversion or performance hit. This allows a developer to mix the expressiveness of Elixir with the robustness and performance of Erlang.

    -

    To install Elixir or learn more about it, check our getting started guide. We also have online documentation available and a Crash Course for Erlang developers.

    +

    To install Elixir or learn more about it, check our getting started guide. We also have online documentation available and a Crash Course for Erlang developers. Or you can just keep on reading for a few code samples!

    -

    Highlights

    +

    Language highlights

    @@ -82,7 +80,7 @@

    Polymorphism via protocols

    {% highlight elixir %} file = File.stream!("README.md") -lines = Enum.map(file, fn(line) -> Regex.replace(%r/"/, line, "'") end) +lines = Enum.map(file, fn(line) -> Regex.replace(~r/"/, line, "'") end) File.write("README.md", lines) {% endhighlight %}
    @@ -148,11 +146,56 @@

    Pattern matching

    +
    +

    Tooling included

    + +
    +

    Elixir ships with a great set of tools to ease development. Mix allows you to easily create your new project, manage tasks and dependencies:

    + +{% highlight text %} +$ mix new my_app +$ cd my_app +$ mix test +. + +Finished in 0.04 seconds (0.04s on load, 0.00s on tests) +1 tests, 0 failures +{% endhighlight %} + +

    In the example above, we have created a new project and ran its initial test suite powered by the ExUnit test framework.

    +
    +
    + +
    +

    Interactive (and remote) shells

    + +
    +

    Elixir also ships with an Interactive Shell, called IEx, which provides a great set of helpers for writing code, like easy access to documentation (shown above), code reloading and so on. It is also a great companion for production, as it allows for example connecting to remote nodes. Here is a quick example you can try locally. In one terminal, do:

    + +{% highlight text %} +$ iex --name hello +iex(hello@machine)1> defmodule Hello do +...(hello@machine)1> def world, do: IO.puts "Distributed hello world" +...(hello@machine)1> end +{% endhighlight %} + +

    See the name in between parenthesis starting with hello? Copy that, open up another terminal and pass it to --remsh (short for remote shell):

    + +{% highlight text %} +$ iex --name world --remsh "hello@machine" +iex(hello@machine)1> Hello.world +"Distributed hello world" +{% endhighlight %} + +

    Notice we were able to invoke a function from a module defined in the other terminal! Even the node names are the same. This will work in between machines in the same network as long as they have the same value in the ~/.erlang.cookie file!

    +
    +
    +

    Erlang all the way down

    -

    After all, Elixir is still Erlang. An Elixir programmer can invoke any Erlang function with no runtime cost:

    +

    After all, Elixir runs in the Erlang VM. An Elixir programmer can invoke any Erlang function with no runtime cost:

    {% highlight elixir %} :application.start(:crypto)