00:00:00
Alright, so I'm Josh Adams, as you said, and I'm going to explain to you as Rubyists.
00:00:05
I'll primarily do this by showing you a lot of the syntax and the primitives.
00:00:11
I'll compare it to Ruby where that's appropriate.
00:00:17
If you've never heard of this language, let me start with a brief introduction.
00:00:24
Who knows who José Valim is? Okay, so a bunch of people.
00:00:30
He's a Rails core developer, very substantially known as a Ruby developer.
00:00:38
In 2011, he took a sabbatical from Rails core and started working on a secret project.
00:00:44
Elixir is that project.
00:00:49
It's a functional programming language built on top of the Erlang VM.
00:00:55
To give you the official Elixir sales pitch,
00:01:00
Elixir is a functional and metaprogramming language built on top of the Erlang VM.
00:01:06
It's a dynamic language with flexible syntax and macro support.
00:01:11
It leverages Erlang's abilities to build concurrent, distributed, and fault-tolerant applications with hot code upgrades.
00:01:17
Alright, that was a bunch of stuff, right?
00:01:24
I'm going to try and touch on all these topics, but there's limited time, so let's go ahead and get started.
00:01:30
I'm not going to kick off this talk saying, "Hey, you should learn Elixir" because I think it's really neat.
00:01:36
But I'm not above getting people whose opinion you might value more than mine to do it for me.
00:01:42
So, Dave Thomas, pragmatic programmer, has repeatedly said that Elixir gives him the feel-goods the way Ruby did when he first started learning it in 1998.
00:01:49
He's seen more than a few programming languages, so he kinda knows what he's talking about.
00:01:55
He's currently writing a book called "Programming Elixir," which is an introductory text for learning Elixir.
00:02:01
Now, I'm going to mention Bruce Tate.
00:02:05
Has anyone here heard of his book "Seven Languages in Seven Weeks"?
00:02:11
He points out that we're making a transition to multi-core.
00:02:17
He goes on to say that if we don't switch to something other than mutable state, things are going to break.
00:02:24
I agree with this absolutely.
00:02:30
I recently built a distributed system in Ruby using Celluloid, which is very cool.
00:02:36
However, building it in Ruby was really frustrating.
00:02:43
Celluloid is great, but Ruby libraries often do really bad things with mutable state.
00:02:49
Typically, they do this in a not concurrency-safe manner.
00:02:56
The killer problem I faced was related to our specs and mocks.
00:03:03
There were host situations where I ended up writing a library instead of using existing Ruby libraries.
00:03:09
One of the big reasons for using Ruby is its incredible library support.
00:03:15
There are libraries to do everything.
00:03:21
But one of the downsides is that none of those libraries care about threads.
00:03:27
I would frequently find a library that did exactly what I wanted, and then upon digging into it,
00:03:33
I discovered it was using a very bad and not thread-safe way.
00:03:39
So I ended up rewriting them constantly.
00:03:46
It's fairly understandable if you attended last year's Rubicon.
00:03:53
Matt said last year that he's not a threading guy.
00:03:59
And that's understandable; we all do our own thing.
00:04:05
But it was incredibly frustrating for me because I was dealing with all of this at the time.
00:04:12
I was really hoping that, when he was asked the question, he'd come up with something amazing.
00:04:20
I was hoping we were going to implement these concurrency primitives in the language.
00:04:27
But that’s not happening for a while.
00:04:34
So that’s the Elixir sales pitch.
00:04:40
Now, why am I qualified to tell you about this stuff?
00:04:46
First off, I'm the CTO of Isotope 11, a software consultancy.
00:04:53
We've been doing Ruby client work for eight years.
00:05:01
We've also become very comfortable with JavaScript, Erlang, and Elixir.
00:05:07
By the way, we have two projects wrapping up this week.
00:05:13
So we're looking for new awesome projects if anybody needs help.
00:05:19
I wouldn’t be talking to you if I didn’t work for this company.
00:05:26
I also run a screencast series called Elixir Sips.
00:05:34
For one of the best questions I receive, I'm going to give away a subscription.
00:05:40
It basically chronicles my journey learning Elixir.
00:05:46
There are currently around 63 videos and about seven hours of footage.
00:05:53
I've been doing this for seven months, so I've spent a considerable amount of time learning Elixir.
00:06:00
Having said that, let's get into it.
00:06:08
In this section, we'll cover some basic concepts.
00:06:15
We'll look at data types, pattern matching, functions, modules, and Mix,
00:06:22
which you can think of as a build tool and dependency manager.
00:06:29
Let's get started.
00:06:35
The data types we'll cover are atoms, numbers, strings, lists, tuples, maps, and regex.
00:06:41
Atoms in Elixir are similar to symbols in Ruby.
00:06:48
They look pretty normal and consist of a colon followed by letters, digits, and underscores.
00:06:54
Atoms are used frequently to tag tuples.
00:07:01
You'll often see them as the first element in a list or tuple.
00:07:07
Strings in Elixir are exactly like strings in Ruby, except they're not objects.
00:07:14
After reading an article on Hacker News about Unicode support in various languages,
00:07:20
you'll be glad to know that Elixir outperforms most other languages in this area.
00:07:27
Elixir's Unicode support is programmatically built at compile time,
00:07:34
based on the Unicode text code points file.
00:07:41
Now, let's discuss basic data types.
00:07:47
You can use underscores in numbers and the compiler will ignore them.
00:07:56
You can add, subtract, and compare them. Floating point numbers can have a decimal point,
00:08:02
but you cannot omit a zero in front of them.
00:08:09
Lists look like arrays in other languages, including Ruby, but they have different semantics.
00:08:18
To create a list, simply put items in square brackets.
00:08:24
The easiest and fastest way to interact with a list is to get its head or its tail.
00:08:30
This means getting either the first element of the list or all of the rest.
00:08:39
Lists are actually linked lists, so there are a couple of built-in functions for accessing the head and tail.
00:08:46
These functions are HD for head and TL for tail.
00:08:53
If you're looking for something similar to arrays, you want tuples.
00:09:00
Tuples are ordered collections enclosed in curly braces.
00:09:06
They are often used in pattern matching and as return values from functions.
00:09:12
While lists are implemented as linked lists, tuples are contiguous areas of memory.
00:09:20
This allows for constant time access, which is useful.
00:09:25
Now let's talk about maps.
00:09:31
Maps are like hashes, and they just landed in Erlang's 17th release candidate.
00:09:37
As of this writing, they aren't even stable in Erlang yet.
00:09:43
However, they're expected to improve performance in future releases.
00:09:50
Updating a map has interesting behavior and syntax.
00:09:56
You can address the items in a map using dot syntax if the keys are all atoms,
00:10:02
or with your access syntax.
00:10:09
The syntax for updating looks a bit quirky at first.
00:10:15
It uses the first element as the map, followed by a pipe, and then the update.
00:10:21
Elixir has Perl-compatible regular expressions.
00:10:27
You can use them with the same syntax you're used to in Ruby.
00:10:35
Regular expressions support more advanced use cases, including named captures.
00:10:41
For example, capturing a section and retrieving it as a key list.
00:10:47
In summary, everything except false or nil is considered truthy in Elixir.
00:10:54
Next, we'll look at pattern matching, which is one of the neatest features of Elixir.
00:11:01
We're going to talk about the match operator, function definitions, and case statements.
00:11:06
The match operator looks like an equal sign.
00:11:13
At first glance, it looks like regular variable assignment, but it's a bit different.
00:11:20
It sets the previously unbound variable to a value.
00:11:27
If you're trying to match with an unbound variable, Elixir will bind the variable so your assertion is true.
00:11:35
If the variable is already bound and you attempt to match a different value, it'll throw an error.
00:11:41
This is a matcher.
00:11:46
You'll see this often as you get started with Elixir.
00:11:52
Now, let's explore how matches work with more complex data structures.
00:12:01
For instance, using an underscore tells the compiler you don't care about a value.
00:12:06
Elixir is a compiled language, as opposed to an interpreted one.
00:12:12
In Elixir, you can rebind variables, which might confuse those used to Erlang.
00:12:19
This allows you to rebind a variable without static single assignment.
00:12:25
If you want to prevent rebinding of a variable, you can put a hat before it to signal this to the compiler.
00:12:31
Pattern matching is also used to choose between different function definitions.
00:12:37
In the example, we define a function differently based on the input.
00:12:44
This is a significant difference from Ruby's approach.
00:12:50
Defining functions based on how they operate based on inputs is exciting, especially for those with a math background.
00:12:56
Next, case statements can be used for control flow in a similar pattern.
00:13:03
There's a clear precedence, moving from top to bottom.
00:13:09
Let's now move on to functions.
00:13:16
In Elixir, functions are first-class types, which shouldn't be surprising since it's a functional language.
00:13:23
We'll discuss defining, calling, and using them as types.
00:13:31
Anonymous functions are defined using the 'fn' keyword.
00:13:37
The parameter lists and bodies are separated by arrows.
00:13:44
The 'print name' function simply concatenates first and last names.
00:13:54
Calling an anonymous function looks a bit unusual at first.
00:14:00
The argument looks similar to calling a function in the module.
00:14:06
However, a dot makes calling it feel different.
00:14:13
It’s under discussion whether this will change.
00:14:19
Initially, I disliked it, but I've grown to accept it.
00:14:27
Now, what happens if you call this function with something that doesn't match any of the parameter lists?
00:14:34
It throws an error indicating 'function clause not matching'.
00:14:40
Now, let's define an anonymous function that behaves differently based on input.
00:14:47
If you provide a list of two items, it returns the sum of their prices.
00:14:54
For a single item, it simply returns the price.
00:15:00
Though a very silly way to define this function, it demonstrates pattern matching.
00:15:07
You can invoke anonymous functions immediately.
00:15:13
This action highlights some fun utility.
00:15:20
Next is a focus on modules and Mix.
00:15:29
Modules are the primary unit of code organization in Elixir.
00:15:36
They function like classes in Ruby.
00:15:43
Modules can contain both private and public functions.
00:15:50
Let’s discuss using Mix to begin new projects.
00:15:56
With Mix, type mix new <project_name> to start a new project.
00:16:03
This is very similar to 'rails new.'
00:16:09
Mix also knows about Git, creating a Git file for you.
00:16:16
It automatically sets up testing, which is first-class in Elixir.
00:16:22
Next, let's look at the mix.exs file generated by Mix.
00:16:29
This file holds project configuration.
00:16:36
You can define dependencies here.
00:16:44
For example, using a tuple that takes a dependency name and a GitHub location.
00:16:51
Mix fetches and compiles the dependencies...
00:16:57
including those from Erlang or even C dependencies.
00:17:02
It's worth noting that the Mix tool is incredibly powerful.
00:17:08
Let's define a trivial example of a module with a single function.
00:17:15
This simple function just returns its argument.
00:17:22
Remember, you can define multiple functions with the same name.
00:17:29
Elixir uses pattern matching to differentiate between the function definitions.
00:17:38
Now, let's compile this module.
00:17:45
You can launch the Elixir shell (IEx) and load the module.
00:17:52
You can also use elixirc to generate a compiled bytecode file.
00:17:58
This bytecode can run on the Erlang VM, allowing interoperability.
00:18:05
To see it in action, define a module in IEx.
00:18:12
This approach is surprising for Erlang users.
00:18:19
You can also define modules directly inside the REPL.
00:18:26
Now let’s explore the last evaluated expression of a module.
00:18:33
This tuple contains details regarding the last action.
00:18:40
Elixir supports module and function documentation.
00:18:46
Documentation can be defined using module attributes.
00:18:52
You can ask for help on any module using a helper in IEx.
00:18:58
This outputs helpful information about the module.
00:19:05
You can also generate HTML documentation.
00:19:12
Those are some of the fundamental insights into basic Elixir.
00:19:20
We've covered data types, pattern matching, functions, and modules.
00:19:27
From here, we'll shift focus to slightly less basic Elixir.
00:19:34
We'll talk about list comprehensions, structs, the pipe operator, recursion, and processes.
00:19:39
The general idea behind list comprehensions is to quickly create a data structure.
00:19:46
For example, from one or more enumerable lists.
00:19:52
Here's a basic example: for each X in a list, return double the X.
00:19:58
It's straightforward! In another example, we return a different structure.
00:20:04
We can also filter lists, only including X's divisible by 2.
00:20:10
List comprehensions can combine lists, resulting in a larger collection.
00:20:16
This functionality makes it more versatile than Ruby.
00:20:22
The output from comprehensions makes them even more interesting.
00:20:28
You can create a list of tuples with conditions based on multiplicative calculations.
00:20:35
Let's define a module for building a deck of cards.
00:20:42
We define the suits and values, returning a card table.
00:20:49
Structs are type-safe maps that can have default values and can be pattern matched against.
00:20:55
Structs are straightforward to define, we just use '__struct__'.
00:21:01
Behind the scenes, a struct is essentially an Erlang map.
00:21:08
Elixir was designed for programmer happiness, similar to Ruby.
00:21:15
For structs, the update syntax allows you to maintain type safety.
00:21:21
For example, we can verify updating the person struct with a last name.
00:21:28
Trying to update a raw map will throw an error.
00:21:34
Pattern matching on structs is easy.
00:21:41
If the last name matches, we greet the brother, otherwise, we do not.
00:21:47
Now, let's discuss the pipe operator, which is quite useful.
00:21:54
The pipe operator allows you to create a sequence of function calls.
00:22:01
In UNIX, a pipeline takes the output from one process as input into another.
00:22:07
Elixir's pipe operator offers similar powerful data transformations.
00:22:14
In code, the pipe operator looks like this: |>
00:22:21
This allows you to pass data through multiple functions seamlessly.
00:22:27
For example, I built a code removal project using Elixir's pipe operator.
00:22:34
The pipeline structure is clear and easy to read.
00:22:40
If we didn’t use the pipe operator, the format becomes cumbersome.
00:22:47
Jose is very focused on creating a syntax that prioritizes readability.
00:22:53
Now let's briefly cover recursion.
00:23:00
Recursive functions call themselves.
00:23:06
For instance, we can write a recursive function that sums a list of numbers.
00:23:12
The primary function has two forms, using an accumulator.
00:23:19
It processes by popping the head and calling itself.
00:23:25
The final form handles an empty input list.
00:23:32
Elixir supports tail call optimization, avoiding stack overflow.
00:23:39
It allows for efficient recursive operations on large lists.
00:23:45
Now let's discuss processes.
00:23:52
In Erlang, a process is a concurrency unit.
00:23:59
Processes share no memory and communicate via asynchronous messaging.
00:24:05
Any message sent is a copy, ensuring no shared state.
00:24:11
In Elixir, there are no blocking processes.
00:24:18
If you create many processes, it runs multi-core by default.
00:24:24
For example, running on a four-core machine distributes processes across cores.
00:24:31
I performed a performance test using Elixir, serving up to 40,000 concurrent requests.
00:24:37
In contrast, Ruby only managed around 300.
00:24:43
Elixir also supports easy distribution across machines.
00:24:50
You can increase system performance by adding up to 15 or 20 machines.
00:24:57
This is an incredible advantage for high-performance systems.
00:25:04
Now, let’s define a new module called ping.
00:25:10
This module contains a function that awaits pong messages.
00:25:17
When it receives a message, it responds with a ping.
00:25:23
When spawning a new process, the process ID is captured.
00:25:30
This ID is essential for communicating via messages.
00:25:36
Processes maintain a mailbox for received messages.
00:25:43
It’s important to understand that messages are stored in a mailbox.
00:25:50
You can spawn processes across different machines seamlessly.
00:25:56
Everything is consistent, whether local or remote.
00:26:03
Modeling mutable state is accomplished through processes.
00:26:09
By modifying the state, you create an object-like variable.
00:26:16
Each message can operate just like calling methods on objects in Ruby.
00:26:23
Like in Ruby, you're sending messages to processes.
00:26:30
We defined a mechanism to communicate and modify state.
00:26:37
Unfortunately, there’s a lot more to explore.
00:26:44
Today we've covered list comprehensions, structs, pipes, recursion, and processes.
00:26:50
There are areas that we didn't have time for, and I think they're important.
00:26:57
For example, testing is vital.
00:27:06
Elixir ships with XUnit, its built-in testing framework.
00:27:13
It's similar to TestUnit in Ruby.
00:27:19
You can also run documentation tests to ensure accuracy.
00:27:25
Elixir's documentation matches the expected output from examples.
00:27:32
This feature is essentially a one-liner to verify documentation.
00:27:39
Let's talk about OTP.
00:27:46
It's a framework for building distributed fault-tolerant systems.
00:27:53
We organize applications into supervision trees that manage processes.
00:28:01
If any process fails, it can be restarted automatically.
00:28:07
This ensures high uptime and stability.
00:28:14
Now, distribution allows processes to be created across different machines.
00:28:20
It's a powerful aspect of both Erlang and Elixir.
00:28:27
That's all for my talk!
00:28:33
I'm now open to any questions you might have.