Articles to read¶
Elixir (programming language) - Wikipedia, the free encyclopedia¶
Features
A language that compiles to bytecode for the Erlang Virtual Machine (BEAM)
Everything is an expression
Erlang functions can be called from Elixir without run time impact, due to compilation to Erlang bytecode, and vice versa
Meta programming allowing direct manipulation of AST
Polymorphism via a mechanism called protocols. Enumerable is an example of a protocol, and is inspired by Clojure reducers
Support for documentation via Python-like docstrings in the Markdown formatting language
Shared nothing concurrent programming via message passing (Actor model)
Emphasis on recursion and higher-order functions instead of side-effect-based looping
Lightweight concurrency utilizing Erlang's mechanisms with simplified syntax (e.g. Task)
Lazy and async collections with streams
Pattern matching
Unicode support and UTF-8 strings
Intro to Elixir Lang - SendGrid¶
We haven’t even scratched the surface in this blog. There are so many amazing features in Elixir, I hope you will go on to check it out for yourself. I’ve listed some learning resources below to help you along the way, but before you head off and dive in, here are some of my personal favourite things about Elixir:
It was easy to grasp, with a ruby-like syntax, and Erlang-like primitives
It’s got a fantastic community behind it
It’s concurrency model is both familiar to me, and very powerful
Mix is a godsend, and saves ALOT of time
Elixir encourages us to document everything
It makes building distributed apps a much-less confusing battlefield
I can see Elixir going a LONG way.
A Week with Elixir - Joe Armstrong¶
This is good shit. The funny thing is that Erlang and Elixir are the same thing under the surface. They “feel” the same to me.
The Pipe operator
This is one of those things that is really really good, and really really easy to understand so nobody will give you credit for it. That’s life.
Sigils are great. Erlang could have had these 15 years ago, and they can be added now without breaking backwards compatibility.
Docstrings are great love ‘em.
Elixir macros are really easy - quote is just quasiquote in lisp and unquote is the list comma operator - so that was easy :-)
This has been my first week with Elixir, and I’m pretty excited.
Elixir has a non-scary syntax and combines the good features of Ruby and Erlang. It’s not Erlang and it’s not Ruby and it has ideas of its own.
It’s a new language, but books are being written as the language is being developed. The first Erlang book came 7 years after Erlang was invented, and the first popular book 14 years later. 21 years is too long to wait for a decent book.
Dave loves Elixir, I think it’s pretty cool, I think we’re going to have fun together.
Erlang powers things like WhatsApp and crucial parts of half the world’s mobile phone networks. It’s going to be great fun to see what will happen when the technology becomes less scary and the next wave of enthusiasts joins the party.
This was written in haste and while excited. So probably has a few typos. Feel free to push corrections.
Elixir - The Love Child of Ruby and Erlang - SitePoint¶
1. The Interactive Elixir Shell
2. Functional
3. The |> operator
4. Processes and Message Passing
5. Erlang Interoperability
Why My Next Programming Language is Elixir - Benjamin Tan¶
Joe, José and Dave certain qualify as “alpha geeks”, and their interest/investment in Elixir is definitely noteworthy.
The |> operator. This operator acts very much like the | operator in Unix shells.
Yes, I could do concurrency in Ruby. There’s Celluloid for example. But, concurrency isn’t something built into Ruby’s DNA.
While no one raves about Erlang’s syntax, there is little doubt that Erlang can handle concurrency remarkably well. With Elixir, we can have the best of both worlds!
Elixir – It’s Not About Syntax - Hacking Devin Torres¶
Tooling¶
iex> defmodule Foo do
...> def bar, do: "bar!"
...> end
{:module,Foo,<<70,79,82,49,0,0,7,24,66,69,65,77,65,116,111,109,0,0,0,102,0,0,0,11,10,69,108,105,120,105,114,46,70,111,111,8,95,95,105,110,102,111,95,95,4,100,111,99,115,9,...>>,{:bar,0}}
iex> Foo.bar
"bar!"
@doc """
Checks if the given argument is nil or not.
Allowed in guard clauses.
## Examples
iex> nil?(1)
false
iex> nil?(nil)
true
"""
$ mix help
mix clean # Clean generated application files
mix compile # Compile source files
mix deps # List dependencies and their status
mix deps.clean # Remove dependencies files
mix deps.compile # Compile dependencies
mix deps.get # Get all out of date dependencies
mix deps.unlock # Unlock the given dependencies
mix deps.update # Update dependencies
mix do # Executes the commands separated by comma
mix escriptize # Generates an escript for the project
mix help # Print help information for tasks
mix local # List local tasks
mix local.install # Install a task locally
mix local.rebar # Install rebar locally
mix local.uninstall # Uninstall local tasks
mix new # Creates a new Elixir project
mix run # Run the given expression
mix test # Run a project's tests
Metaprogrammability¶
iex> contents = quote do
...> defmodule HelloWorld do
...> def hello_world do
...> IO.puts "Hello world!"
...> end
...> end
...> end
{:defmodule,[context: Elixir],[{:__aliases__,[alias: false],[:HelloWorld]},[do: {:def,[context: Elixir],[{:hello_world,[],Elixir},[do: {\{:.,[],[{:__aliases__,[alias: false],[:IO]},:puts]},[],["Hello world!"]}]]}]]}
iex> Code.eval_quoted contents
{{:module,HelloWorld,<<70,79,82,49,0,0,7,104,66,69,65,77,65,116,111,109,0,0,0,132,0,0,0,13,17,69,108,105,120,105,114,46,72,101,108,108,111,87,111,114,108,100,8,95,95,105,110,102,111,95,...>>,{:hello_world,0}},[]}
iex> HelloWorld.hello_world
Hello world!
:ok
defmacro match?(left, right) do
quote do
case unquote(right) do
unquote(left) ->
true
_ ->
false
end
end
end
iex> list = [{:a,1},{:b,2},{:a,3}]
[a: 1, b: 2, a: 3]
iex> Enum.filter list, fn (thing) -> match?({:a, _}, thing) end
[a: 1, a: 3]
iex> Enum.filter list, match?({:a, _}, &1)
[a: 1, a: 3]
defmodule MimeTypes do
HTTPotion.start
HTTPotion.Response[body: body] = HTTPotion.get "http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types"
Enum.each String.split(body, %r/\n/), fn (line) ->
unless line == "" or line =~ %r/^#/ do
[ mimetype | _exts ] = String.split(line)
def is_valid?(unquote(mimetype)), do: true
end
end
def is_valid?(_mimetype), do: false
end
MimeTypes.is_valid?("application/vnd.exn") #=> false
MimeTypes.is_valid?("application/json") #=> true
A brief glimpse at the standard library and runtime¶
defexception Exn.EncodeError, value: nil do
def message(exception), do: "#{inspect exception.value} cannot be encoded"
end
defprotocol Exn.Encoder do
def encode(term)
end
defimpl Exn.Encoder, for: [Atom, List, Number, BitString, Regex] do
def encode(term), do: inspect(term)
end
defimpl Exn.Encoder, for: Range do
def encode(Range[first: f, last: l]), do: "#{f}..#{l}"
end
defimpl Exn.Encoder, for: Tuple do
def encode(term) do
inspect(term, raw: true)
end
end
defimpl Exn.Encoder, for: [PID, Function, Reference, Port] do
def encode(term), do: raise Exn.EncodeError, value: term
end
iex> Exn.encode 1..5
"1..5"
iex> Exn.encode %r{.*}
"%r\".*\""
Elixir : Erlang :: Clojure : Java¶
Elixir is the power of it’s tooling, the expressiveness of it’s metaprogrammability, and the expansive feature set of it’s standard library while maintaining complete compatibility with—and heavily leveraging—OTP.
Tutorial – Build a Web App Using Elixir and Dynamo With Streaming and Concurrency¶
To create your first app and get dependencies¶
# Clone repo and enter its folder
git clone git://github.com/elixir-lang/dynamo.git
cd dynamo
# Get dependencies and run tests
MIX_ENV=test mix do deps.get, test
# Create the project
mix dynamo ~/storyteller
cd ~/storyteller
# Install dependencies. Similar to bundler. Nice!
mix deps.get
# Start the server. By default in localhost:4000
mix server
web/routers/application_router.ex¶
defmodule ApplicationRouter do
use Dynamo.Router
prepare do
# Pick which parts of the request you want to fetch
# You can comment the line below if you don't need
# any of them or move them to a forwarded router
conn.fetch([:cookies, :params])
end
get "/" do
conn = conn.assign(:title, "Welcome to Concurrent Story Teller v0.1!")
render conn, "index.html"
end
end
web/templates/index.html.eex¶
<!DOCTYPE HTML>
<html>
<head>
<title><%= @title %></title>
<link href="/static/favicon.ico" rel="icon">
<link href="/static/storyteller.css" media="all" rel="stylesheet" type="text/css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script src="/static/storyteller.js"></script>
</head>
<body>
<div id="main-container">
<h1><%= @title %></h3>
<section class="stories-list">
<h2>Please, choose a story</h3>
<article class="story">
<h3>Red Riding</h3>
<a href="/red-riding">Read it to me!</a>
</article>
<article class="story">
<h3>Snow white</h3>
<a href="/snow-white">Read it to me!</a>
</article>
<article class="story">
<h3>Cinderella</h3>
<a href="/cinderella">Read it to me!</a>
</article>
</section>
<div>
</body>
</html>
web/routers/application_router.ex¶
# ....
get "/:file_name" do
normalized_title = String.capitalize(String.replace(conn.params[:file_name], "-", " "))
conn = conn.assign :title, normalized_title
conn = conn.assign :file_name, conn.params[:file_name]
render conn, "story.html"
end
end
web/layouts/story.html.eex¶
<!DOCTYPE HTML>
<html>
<head>
<title><%= @title %></title>
<link href="/static/favicon.ico" rel="icon">
<link href="/static/storyteller.css" media="all" rel="stylesheet" type="text/css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script src="/static/storyteller.js"></script>
</head>
<body>
<div id="main-container">
<h2><%= @title %></h3>
<section id="chat">
</section>
<a class="play" href="/play/<%= @file_name %>">Play!</a>
<div>
</body>
</html>
web/routers/application_router.ex¶
# ....
get "/play/:story_name" do
conn = conn.resp_content_type("text/event-stream")
conn = conn.send_chunked(200)
iterator = File.iterator!("#{conn.params[:story_name]}.txt")
Enum.each iterator, fn(line) ->
{ :ok, conn } = conn.chunk "data: #{line}\n"
await conn, 1000, on_wake_up(&1, &2), on_time_out(&1)
end
conn
end
defp on_wake_up(arg1, arg2) do
# Nothing
end
defp on_time_out(arg1) do
# Nothing
end
Now let’s test out code. The curl command is perfec for this¶
curl -i http://localhost:4000/play/cinderella
HTTP/1.1 200 OK
transfer-encoding: chunked
connection: keep-alive
server: Cowboy
date: Mon, 29 Apr 2013 15:12:27 GMT
content-type: text/event-stream; charset=utf-8
cache-control: max-age=0, private, must-revalidate
data: Once upon a time... there lived an unhappy young girl.
data: Unhappy she was, for her mother was dead, her father
data: had married another woman, a widow with two daughters,
data: and her stepmother didn't like her one little bit. All
data: the nice things, kind thoughts and loving touches were
data: for her own daughters. And not just the kind thoughts
# ...
/priv/static/storyteller.js¶
$(function(){
$('.play').on('click', function(ev){
ev.preventDefault();
var source = new EventSource(this.href);
source.addEventListener('message', function(e) {
var $line = $('<p class="story-line">' + e.data + '</p>');
$line.appendTo('#chat').hide().fadeIn(200, function(){
$("html, body").animate({ scrollTop: $(document).height() }, "slow");
})
}, false);
});
});
Elixir is for programmers - Pluralsight blog¶
Smart assert¶
Elixir knows that == is being used in the assertion, and shows you the values on either side of == when the test fails. Now that’s a useful error!
Multi-block control flow¶
In Elixir, the relationship between single line functions and multi-line blocks is well thought out.
These two are equivalent:
# Single line
if(condition, do: a, else: b)
# Multiple lines
if condition do
a
else
b
end
In the single line version, if is a function that takes two arguments: the condition and the clauses. The clauses are do and else.
Consistent use of do¶
Elixir is consistent.
Need a module? It’s defmodule...do. Need an if? It also uses do. Same with def.
Built-in TDD¶
With Elixir, it’s built in. Use the mix command to generate a new app and you’re ready to go with a unit test. Run mix test to run the test suite. Done.
And it can run your tests concurrently with a single async option.
Mind-blowing metaprogramming: upcase¶
def upcase("é" <> rest) do
["É"] ++ upcase(rest)
end
A few things are going on here. Elixir can match functions on the number, type, and content of its arguments. So it looks for a letter such as é. It knows the upper case version of the letter, then sends the rest of the string to the next letter’s upcase function.
Elixir Isn’t Hipster - alexrp’s blog¶
Performance¶
It uses the so-called ‘threaded code interpretation’ approach. This is one of the fastest possible ways to interpret bytecode as every instruction is a direct pointer to executable C code.
the BEAM offers a full-blown native code compiler these days, called HiPE (High Performance Erlang). HiPE works in a similar fashion to software such as PyPy and IonMonkey with the exception that it compiles ahead-of-time (AOT) instead of just-in-time (JIT).
Reliability¶
- Linked processes
- Coordinated restarts
- Failover nodes
- Hot code reloading
Language Design¶
- Expression syntax
- Pattern matching
- First-class functions
- Closures
- Records
- Protocols
- Metaprogramming
- Unicode strings
- Immutability
- Variable rebinding
- Erlang interop
Functional Programming¶
A general rule of thumb is that you should focus on the data flowing through your program rather than focusing on your program’s high-level behavior.
once you stop thinking about objects and methods entirely, it becomes incredibly easy to solve real problems in functional languages.
The Erlangelist – Why Elixir?¶
The problems of Erlang the language¶
The problem I have with Erlang is that the language is somehow too simple, making it very hard to eliminate some frequent cases of boilerplate and structural duplication. Consequently, the resulting code gets a bit messy, being harder to write, analyze, and modify.
What Elixir is (not)¶
it is an Erlang-esque language with improved code organization capabilities.
Metaprogramming¶
Elixir macros are nothing like C/C++ macros. Instead of working on strings, they are something like compile-time Elixir functions that are called in the middle of parsing, and work on the abstract syntax tree (AST), which is a code represented as Elixir data structure. Macro can work on AST, and spit out some alternative AST that represents the generated code. Since macros are executed in compile-time, the performance of a running system will not be affected.
Pipeline operator¶
The code can easily be followed by reading it from top to bottom.
The pipeline operator works extremely well because the API in Elixir libraries follows the "subject (noun) as the first argument" convention.
Polymorphism via protocols¶
Protocols allow developers to create a generic logic that can be used with any type of data, assuming that some contract is implemented for the given data.
The mix tool¶
With a single command line you can create an OTP application skeleton, that consists of only 7 files (this includes .gitignore and README.md).
Other goodies¶
there are many other enhancements Elixir provides, such as support for variable rebinding, optional parentheses, implicit statement endings, nullability, short circuits operators, ...
Introduction to Parallel Computing with Elixir - Reactive.IO¶
Mixing Up a New Project¶
mix new fibex
/lib/fibex/fn.ex¶
defmodule Fibex.Fn do
def run(n) do
run(n, 1, 0)
end
defp run(0, _, _) do
0
end
defp run(1, a, b) do
a + b
end
defp run(n, a, b) do
run(n - 1, b, a + b)
end
end
/lib/fibex.ex¶
defmodule Fibex do
def run(ns) do
ns
|> Enum.map(&(Fibex.Fn.run(&1)))
|> inspect
|> IO.puts
end
end
$ mix run -e "Fibex.run([20, 18, 32, 28, 22, 42, 55, 48])"
[6765, 2584, 2178309, 317811, 17711, 267914296, 139583862445, 4807526976]
Going Parallel¶
/lib/fibex/fn.ex¶
defmodule Fibex.Fn do
def spawn_run(pid, ni) do
spawn __MODULE__, :send_run, [pid, ni]
end
def send_run(pid, {n, i}) do
send pid, {run(n), i}
end
def run(n) do
run(n, 1, 0)
end
defp run(0, _, _) do
0
end
defp run(1, a, b) do
a + b
end
defp run(n, a, b) do
run(n - 1, b, a + b)
end
end
/lib/fibex.ex¶
defmodule Fibex do
def run(ns) do
ns
|> Enum.with_index
|> Enum.map fn(ni) ->
Fibex.Fn.spawn_run(self, ni)
end
receive_fibs(length(ns), [])
end
defp receive_fibs(lns, result) do
receive do
fib ->
result = [fib | result]
if lns == 1 do
IO.puts(print_fibs(result))
else
receive_fibs(lns - 1, result)
end
end
end
defp print_fibs(fibs) do
fibs
|> Enum.sort(fn({_, a}, {_, b}) -> a < b end)
|> Enum.map(fn({f, _}) -> f end)
|> inspect
end
end
$ mix run -e "Fibex.run([20, 18, 32, 28, 22, 42, 55, 48])"
[6765, 2584, 2178309, 317811, 17711, 267914296, 139583862445, 4807526976]
Building our Escript¶
/lib/fibex.ex¶
defmodule Fibex do
def main(argv) do
argv |> Enum.map(&(String.to_integer(&1))) |> run
end
def run(ns) do
ns
|> Enum.with_index
|> Enum.map fn(ni) ->
Fibex.Fn.spawn_run(self, ni)
end
receive_fibs(length(ns), [])
end
defp receive_fibs(lns, result) do
receive do
fib ->
result = [fib | result]
if lns == 1 do
IO.puts(print_fibs(result))
else
receive_fibs(lns - 1, result)
end
end
end
defp print_fibs(fibs) do
fibs
|> Enum.sort(fn({_, a}, {_, b}) -> a < b end)
|> Enum.map(fn({f, _}) -> f end)
|> inspect
end
end
mix.exs¶
defmodule Fibex.Mixfile do
use Mix.Project
def project do
[app: :fibex,
version: "0.0.1",
elixir: "~> 1.0",
escript: [main_module: Fibex],
deps: deps]
end
# Configuration for the OTP application
#
# Type `mix help compile.app` for more information
def application do
[applications: [:logger]]
end
# Dependencies can be Hex packages:
#
# {:mydep, "~> 0.3.0"}
#
# Or git/path repositories:
#
# {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
#
# Type `mix help deps` for more examples and options
defp deps do
[]
end
end
$ ./fibex 20 18 32 28 22 42 55 48
[6765, 2584, 2178309, 317811, 17711, 267914296, 139583862445, 4807526976]
The Oozou Blog - Why we are excited about Elixir¶
The platform¶
Erlang is powering projects like Facebook Chat, WhatsApp, and Amazon SimpleDB, which demonstrates both the scalability and stability of the platform. It’s pretty exciting to imagine that Elixir may be able to provide many new developers with easier access to it.
Ruby-like syntax¶
He also added a certain symmetry that Ruby is lacking (e.g. do keywords for class and module definitions), which allowed for an easier implementation of Elixir’s macro system
Elixir offers hygienic macros. While it’s an old saying amongst Lispers that the first rule of macros is to not write macros, they do occasionally come in handy and add powerful meta-programming and DSL capabilities to the language.
Good tooling¶
mix¶
mix clean # Delete generated application files
mix cmd # Executes the given command
mix compile # Compile source files
mix deps # List dependencies and their status
mix deps.clean # Remove the given dependencies' files
mix deps.compile # Compile dependencies
mix deps.get # Get all out of date dependencies
mix deps.unlock # Unlock the given dependencies
mix deps.update # Update the given dependencies
mix new # Create a new Elixir project
mix run # Run the given file or expression
mix test # Run a project's tests
ExUnit¶
doc tests¶
defmodule Oozou do
@doc ~S"""
Adds two numbers
## Examples
iex> Oozou.add(2, 2)
4
"""
def add(a, b), do: a + b
end
defmodule OozouTest do
use ExUnit.Case, async: true
doctest Oozou
end
Great documentation¶
defmodule Oozou do
@moduledoc """
We handcraft beautiful web apps
"""
@doc """
Be awesome!
"""
def work(), do: :beautiful_web_app
end
Now show me some code¶
defmodule FizzBuzz do
def compute(n) do
s = case {rem(n, 3), rem(n, 5)} do
{0, 0} -> :FizzBuzz
{0, _} -> :Fizz
{_, 0} -> :Buzz
_ -> n
end
IO.puts(s)
end
end
Enum.map(1..15, &FizzBuzz.compute/1)
# or as list comprehension:
# for i <- 1..15, do: FizzBuzz.compute(i)
Lispy Elixir | 8th Light¶
Homoiconicity¶
Elixir, on the surface, is not homoiconic. However, the syntax on the surface is just a facade for the homiconic structure underneath. To peer under the covers we're going to need explore one more of Lisp's ideas.
Quoting¶
In Elixir, all expressions are represented as three element tuples. Let's look at a simple, straightforward example where we are simply adding two numbers.
quote do
1 + 1
end # => {:+, [], [1, 1]}
Abstract Syntax Trees¶
Abstract syntax trees are simply a way to represent call structures in a tree like fashion.
Macros¶
A macro takes quoted forms (abstract syntax trees) and is free to edit the tree as it chooses in order to do any number of interesting things.
Macros (Almost) All The Way Down¶
def in Elixir is a macro. Upon further inspection we can see what def produces in terms of an abstract syntax tree.
Standing On The Shoulders Of Giants¶
Elixir's future looks bright and exciting in part because it's standing on the shoulders of giants.
How I Start – Elixir¶
Portal¶
Installation¶
Our first project¶
Pattern matching¶
Modeling portal doors with Agents¶
Inspecting portals with Protocols¶
Shooting supervised doors¶
Distributed transfers¶
Wrapping up¶
Jose Valim,Rubyにおける並行プログラミングのためのいくつかのアイデアを提案。~ RubyKaigi 2013 基調講演 2日目¶
自己紹介¶
ほかの言語から学ぶこと - Rubyへの提案¶
Javaに目を向けてみる¶
提案その1:Hash#concurrent_read, Hash#concurrent_write¶
提案その2:AtomicReferenceの導入¶
提案その3:Go言語の紹介と軽量で高機能な新しいQueueの採用¶
提案その4:さらに軽量な,並行プログラミングのための仕組みを組み込みで提供する¶
結論 - concurrentなRubyのための議論をしよう¶
Elixir : Erlang VM 上で動作する Ruby 風味の関数型言語 - プログラマーズ雑記帳¶
Elixirとは¶
特徴¶
Hello, world!¶
全てが式¶
メタプログラミングと DSL¶
プロトコル(文脈, 約束)による多様性¶
ドキュメントもコードの一部¶
パターンマッチング¶
結局のところ Erlang¶
インストール¶
Emacs モードの設定¶
感想¶
参考¶
elixir はプログラマの万能薬になるか - Fat Old Sun¶
特徴¶
ひとことでまとめると、erlangの並列/高信頼フレームワークが利用できるrubyライクの構文を持つlispといえる。
ビルド¶
Hello, world.¶
Rubyist が今すぐ Elixir を使ってみるべき理由 - m.igrs.jp¶
パターンマッチング (Pattern matching)¶
単一代入変数と不変性 (Single assignment variables and immutability)¶
プロセス間通信 (Communication between processes)¶
異なるオブジェクトモデル (A different Object Model)¶
学ぶ (Just learn)¶
結論 (Wrapping up)¶
The Excitement of Elixir - Hacking Devin Torres¶
Syntax¶
Elixir syntax is like a marriage of DSL friendly Ruby and the powerful hygenic macros of Clojure.
Expressions¶
Elixir if expressions, like most everything else in the language, are implemented using a macro.
Strings¶
Elixir strings are UTF8 binaries, with all the raw speed and memory savings that brings. Elixir has a String module with Unicode functionality built-in and is a great example of writing code that writes code. String.Unicode reads various Unicode database dumps such as UnicodeData.txt to dynamically generate Unicode functions for the String module built straight from that data!
Single assignment¶
There is no context dependent expression separators and when you rebind a name all you’re doing is rebinding a new value to the name. Elixir is still immutable, it’s just not trying to pass off single assignment as immutability.
Records¶
Elixir records are real records, and provide compile time pattern matching and a much more dynamic nature.
Standard library¶
While you can’t escape using the Erlang standard library from time to time, Elixir’s standard library is trying to normalize zero-based access and noun-first argument ordering, along with a more consistent API.
Code organization¶
Elixir modules provide a great way to encapsulate functionality. As many modules as you desire can be declared within the same file including private functionality (functions, macros, records, exceptions, fuzzybunnies, whatever).