MacMusic  |  PcMusic  |  440 Software  |  440 Forums  |  440TV  |  Zicos
elixir
Recherche

Intro to Elixir: A fresh take on functional programming

mercredi 19 février 2025, 10:00 , par InfoWorld
Elixir is one of the most interesting stories in the world of programming languages right now. Built on the Erlang ecosystem, Elixir offers a fresh take on functional programming and new ways of thinking about concurrency and fault tolerance.

We’ll start with a quick review of the overall syntactic flavor of Elixir and how it handles collections. Then, we’ll look at how it approaches pattern matching, looping, and concurrency. These are standard features for any programming language, but Elixir’s approach is different from what you might expect.

Getting started with Elixir

To start, you’ll need to install Elixir. This gives you the IEx CLI which you can use to run Elixir in REPL mode. Follow the examples by typing them directly into your console.

Every language has conventions for doing everyday things, like the convention of using slashes for comments. Here’s how Elixir handles string concatenation:

'Hello ' 'InfoWorld'

This approach gives us a distinct operator rather than overloading the plus (+) sign. To output to standard out, we enter:

IO.puts('Hello ' 'InfoWorld')

IO.puts() is the way you output to the console in Elixir.

If we wanted to create a variable for this string, Elixir doesn’t require any keywords, we just declare it:

my_var = “Hello Infoworld”

Collections

Now let’s see how Elixir handles collections. Here’s a collection of strings:

books = [
'The Bhagavad Gita',
'Tao Te Ching',
'The Bible',
'The Quran',
'

This array syntax (technically a List in Elixir) is so universal it doesn’t require explanation. But note that arrays are immutable in Elixir—a functional programming tenant that makes the overall system less error-prone and more amenable to concurrency.

Although the List is immutable, Elixir’s syntax lets you perform some operations like add and remove in a way that looks mutable. But under the hood, a new List is created:

books ++ ['The Gospel of John in the Light of Indian Mysticism']

Making immutable collections faster
Immutable collections have performance implications. The underlying virtual machine has tricks up its sleeve to help mitigate these, like structural sharing (where pointers are modified instead of data copied) and Erlang Term Storage can be used when large collections must be modified. Often, streaming can be used to process collection in-line.

Now, let’s say we want to count the letters in each string and make a collection of those integers. We can use some functional programming magic to do it:

book_lengths = Enum.map(books, &String.length/1)

The map function is pretty common and widely used. In JavaScript, where we use it to run a given function over a collection. Elixir introduces some unique features to map. The /1 tells us which overloaded version of String.length to use, and in this case we use the single-argument version (technically, this is called the arity). The & character is the “function capture” operator that gives us a handle on the String.length function (this is loosely analogous to Java’s method reference operator “::”).

To print this out we can use the IO.inspect function for pretty printing:

IO.inspect(book_lengths)
[17, 12, 9, 9, 21]

Elixir’s collection types

We’ve already seen the List type in action. Elixir includes these main collection types:

List: An immutable, but designed for modification-by-duplication, homogenous collection of arbitrary types.

Syntax: Square brackets with items:  [x,y,z]

Tuple: Designed for holding values primarily, not manipulation, tuples are like lists but geared towards read performance.  Think of them a data access kind of collection.

Syntax: Curly braces with items: {x,y,z}

Keywords List: Ordered key-value pairs, string-only keys, mainly used for named arguments for functions

Syntax: Square brackets with pairs: {x: x1,y: y1,z: z1}

Maps: The familiar key-value pairs, where keys can be anything and the collection is unordered.

Syntax: Percent curly braces with pairs:

%{x => x1, y => y1, z => z1}

%{x: x1, y: y1, z: z1}

Maps and atoms

Maps have two styles of declaration, and the one to use depends on whether the keys are atoms. An atom is a variable whose value is the same as its name, a kind of super-constant. An atom is declared with a colon followed by a literal.

We could create a map of string keys to integer values like so:

books_and_lengths = %{ 'The Bhagavad Gita' => 17, 'Tao Te Ching' => 12 }

The following is different, and creates a map of atoms to integers, probably not what we want in this case:

books_and_lengths = %{ 'The Bhagavad Gita': 17, 'Tao Te Ching': 12 }

Note the placement of the colon. In a Map, the colon being directly next to the kay indicates it’s an atom, and atoms can be quote-enclosed (to support otherwise illegal characters).

The bottom line is to use the arrow syntax (=>) when you want a normal variable and the key and the colon (:) when you want atoms.

Normally, atoms are declared like this::my_atom

Here’s another way to declare a map with atom keys:

my_map = %{:atom1 => “foo”,:atom2 => “bar”}

Modules

Elixir supports modules, which are namespaces that gather together related functions. It does not hold state or variables like a class or code block. As you’d expect, you can call other functions from within the same module, but those calling in from outside need to preface the calls or import the module.

Here’s a simple module:

defmodule BookFunctions do
def myFunc
end
end

BookFunctions.myFunc()

Pattern matching

Syntactic flavors and standard library characteristics go a long way to making up the overall feeling of using a language. They are the commonplace features you interact with all the time. But every language has some features that stand out.

Functional pattern matching is a sophisticated and lovely feature that Elixir brings to the table, allowing you to perform conditional function execution in a switch-like syntax. Let’s say we want to output small, medium, or long based on the book title lengths:

defmodule BookFunctions do
def categorize_length(length) do
case length do
length when length 'Short'
length when length <= 20 -> 'Medium'
_ -> 'Long'
end
end

def print_categories(lengths) do
Enum.each(lengths, fn length ->
category = categorize_length(length)
IO.puts('#{length} characters: #{category}')
end)
end
end

A couple of notes:

BookFunctions is a module, which you’ve seen.

In Elixir, return statements are implied, so the categorize_length() function automatically returns whatever is the result of the last expression.

The case keyword is what creates the pattern-matching block, in the categorize_length function. The length when length syntax (technically, a guard clause) lets us do range checking on the length variable, and if it meets the criteria, the -> operator tells us what to return from the case. (Since this is the final statement of the function, it will also be the functional return value.)

We could use this new function on our book_lengths like so:

BookBookFunctions.print_categories(book_lengths)
17 characters: Medium
12 characters: Medium
9 characters: Short
9 characters: Short
21 characters: Long

Enum.each is analogous to forEach in other languages like JavaScript, letting us perform an operation on each element of a collection.

Looping

Elixir does not have for and while loops. This can be a bit shocking at first, but it is in line with the immutability favored by functional philosophy. In essence, Elixir doesn’t want you doing mutation during loops, and instead wants you to use recursion. Recursion keeps you in the realm of functions, and ideally, you want to use pure functions (meaning, functions without side-effects).

Much of the looping you need to do can be handled with functional operations like Enum.each and Enum.map. The Elixir forum has a good, extensive discussion of looping and alternatives.

Comprehensions

One of the most direct ways to simulate a for loop is with comprehensions, which you would be forgiven for mistaking for an actual for loop:

for x
See the Elixir docs for more on comprehensions and how they simplify collection operations.

Pipe operator

The pipe operator gives you a clean syntax for chaining function results together. Think of it as a more elegant form of nesting functions. Here’s a simple example of the pipe operator on our books_and_lengths collection:

books_and_lengths
|> Map.keys()
|> Enum.map(&String.upcase/1)
|> Enum.join(', ')
|> IO.puts()

The output is:

The Bhagavad Gita, Tao Te Ching

Concurrency

Although concurrency is a complex topic, it’s one of Elixir’s areas of strength, so let’s take a quick look. Elixir uses Actors, something like virtual threads in that they are not full operating-system processes. Actors support message-passing for simplified concurrent communication.

The following example, demonstrating message handling, is from the Elixir docs:

defmodule Example do
def listen do
receive do
{:ok, 'hello'} -> IO.puts('World')
end

listen()
end
end

Notice that the listen function is recursive (it calls itself at the end), which lets it handle multiple messages. Without the recursion the process would exit.

To launch this Actor, we use spawn:

pid = spawn(Example,:listen, [])

Then we can send a message from the main process, using the pid we saved:

send pid, {:ok, 'hello'}

This outputs “World” to the console.

Conclusion

Languages are defined largely by what they make easy and what they make hard. Elixir is clearly devoted to making it easy for a programmer to stay in the functional programming mentality, and harder to stray into mutations and side-effects. 

The overall effect is that you tend to write good functional programming code, as long as you work with the language and don’t fight it. It’s not hard to see why Elixir has captured so much interest, bringing Erlang’s legacy into the modern world. It’s a programming language with strong ideas about how to do things and an active, enthusiastic community.
https://www.infoworld.com/article/3820034/intro-to-elixir-a-fresh-take-on-functional-programming.htm

Voir aussi

News copyright owned by their original publishers | Copyright © 2004 - 2025 Zicos / 440Network
Date Actuelle
ven. 21 févr. - 19:42 CET