Elixir is a dynamic, functional language for building scalable and maintainable applications
Application.compile_env/3
inside module attributesMacro.expand_literals/2
and Macro.expand_literals/3
:close_stdin
to System.shell/2
--all-warnings
option:uniq
is given in for
comprehensions and the result is unused@enforce_keys
attribute after defstruct
declaration:debug_info
chunkMacro.to_string/2
when converting an AST with :erlang.binary_to_atom/2
String.split/3
and String.next_grapheme/1
returning invalid results on invalid UTF-8 encodingSystem.shell/2
uri.port
as :undefined
in certain cases in URI.new/1
:moduledoc
and functions are specified in :only
--no-pry
is given.formatter.exs
so they are properly re-evaluted on every callElixir v1.14 brings many improvements to the debugging experience in Elixir
and data-type inspection. It also includes a new abstraction for easy
partitioning of processes called PartitionSupervisor
, as well as improved
compilation times and error messages.
Elixir v1.14 is the last version to support Erlang/OTP 23. Consider updating to Erlang/OTP 24 or Erlang/OTP 25.
dbg
Kernel.dbg/2
is a new macro that's somewhat similar to IO.inspect/2
, but
specifically tailored for debugging.
When called, it prints the value of whatever you pass to it, plus the debugged code itself as well as its location. This code:
# In my_file.exs
feature = %{name: :dbg, inspiration: "Rust"}
dbg(feature)
dbg(Map.put(feature, :in_version, "1.14.0"))
Prints this:
$ elixir my_file.exs
[my_file.exs:2: (file)]
feature #=> %{inspiration: "Rust", name: :dbg}
[my_file.exs:3: (file)]
Map.put(feature, :in_version, "1.14.0") #=> %{in_version: "1.14.0", inspiration: "Rust", name: :dbg}
dbg/2
can do more. It's a macro, so it understands Elixir code. You can see
that when you pass a series of |>
pipes to it. dbg/2
will print the value
for every step of the pipeline. This code:
# In dbg_pipes.exs
__ENV__.file
|> String.split("/", trim: true)
|> List.last()
|> File.exists?()
|> dbg()
Prints this:
$ elixir dbg_pipes.exs
[dbg_pipes.exs:5: (file)]
__ENV__.file #=> "/home/myuser/dbg_pipes.exs"
|> String.split("/", trim: true) #=> ["home", "myuser", "dbg_pipes.exs"]
|> List.last() #=> "dbg_pipes.exs"
|> File.exists?() #=> true
dbg/2
supports configurable backends. IEx automatically replaces the default
backend by one that halts the code execution with IEx.Pry
, giving developers
the option to access local variables, imports, and more. This also works with
pipelines: if you pass a series of |>
pipe calls to dbg
(or pipe into it at the
end, like |> dbg()
), you'll be able to step through every line in the pipeline.
You can keep the default behaviour by passing the --no-pry
option to IEx.
PartitionSupervisor
is a new module that implements a new supervisor type. The
partition supervisor is designed to help with situations where you have a single
supervised process that becomes a bottleneck. If that process's state can be
easily partitioned, then you can use PartitionSupervisor
to supervise multiple
isolated copies of that process running concurrently, each assigned its own
partition.
For example, imagine you have an ErrorReporter
process that you use to report
errors to a monitoring service.
# Application supervisor:
children = [
# ...,
ErrorReporter
]
Supervisor.start_link(children, strategy: :one_for_one)
As the concurrency of your application goes up, the ErrorReporter
process
might receive requests from many other processes and eventually become a
bottleneck. In a case like this, it could help to spin up multiple copies of the
ErrorReporter
process under a PartitionSupervisor
.
# Application supervisor
children = [
{PartitionSupervisor, child_spec: ErrorReporter, name: Reporters}
]
The PartitionSupervisor
will spin up a number of processes equal to
System.schedulers_online()
by default (most often one per core). Now, when
routing requests to ErrorReporter
processes we can use a :via
tuple and
route the requests through the partition supervisor.
partitioning_key = self()
ErrorReporter.report({:via, PartitionSupervisor, {Reporters, partitioning_key}}, error)
Using self()
as the partitioning key here means that the same process will
always report errors to the same ErrorReporter
process, ensuring a form of
back-pressure. You can use any term as the partitioning key.
A common and practical example of a good use case for PartitionSupervisor
is
partitioning something like a DynamicSupervisor
. When starting many processes
under it, a dynamic supervisor can be a bottleneck, especially if said processes
take a long time to initialize. Instead of starting a single DynamicSupervisor
,
you can start multiple:
children = [
{PartitionSupervisor, child_spec: DynamicSupervisor, name: MyApp.DynamicSupervisors}
]
Supervisor.start_link(children, strategy: :one_for_one)
Now you start processes on the dynamic supervisor for the right partition. For instance, you can partition by PID, like in the previous example:
DynamicSupervisor.start_child(
{:via, PartitionSupervisor, {MyApp.DynamicSupervisors, self()}},
my_child_specification
)
Erlang/OTP 25 improved errors on binary construction and evaluation. These improvements apply to Elixir as well. Before v1.14, errors when constructing binaries would often be hard-to-debug generic "argument errors". With Erlang/OTP 25 and Elixir v1.14, more detail is provided for easier debugging. This work is part of EEP 54.
Before:
int = 1
bin = "foo"
int <> bin
#=> ** (ArgumentError) argument error
Now:
int = 1
bin = "foo"
int <> bin
#=> ** (ArgumentError) construction of binary failed:
#=> segment 1 of type 'binary':
#=> expected a binary but got: 1
Elixir v1.12 introduced stepped ranges, which are ranges where you can specify the "step":
Enum.to_list(1..10//3)
#=> [1, 4, 7, 10]
Stepped ranges are particularly useful for numerical operations involving
vectors and matrices (see Nx, for example).
However, the Elixir standard library was not making use of stepped ranges in its
APIs. Elixir v1.14 starts to take advantage of steps with support for stepped
ranges in a couple of functions. One of them is Enum.slice/2
:
letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
Enum.slice(letters, 0..5//2)
#=> ["a", "c", "e"]
binary_slice/2
(and binary_slice/3
for completeness) has been added to the
Kernel
module, that works with bytes and also support stepped ranges:
binary_slice("Elixir", 1..5//2)
#=> "lxr"
Inspect
improvementsIn Elixir, it's conventional to implement the Inspect
protocol for opaque
structs so that they're inspected with a special notation, resembling this:
MapSet.new([:apple, :banana])
#MapSet<[:apple, :banana]>
This is generally done when the struct content or part of it is private and the
%name{...}
representation would reveal fields that are not part of the public
API.
The downside of the #name<...>
convention is that the inspected output is not
valid Elixir code. For example, you cannot copy the inspected output and paste
it into an IEx session.
Elixir v1.14 changes the convention for some of the standard-library structs.
The Inspect
implementation for those structs now returns a string with a valid
Elixir expression that recreates the struct when evaluated. In the MapSet
example above, this is what we have now:
fruits = MapSet.new([:apple, :banana])
MapSet.put(fruits, :pear)
#=> MapSet.new([:apple, :banana, :pear])
The MapSet.new/1
expression evaluates to exactly the struct that we're
inspecting. This allows us to hide the internals of MapSet
, while keeping
it as valid Elixir code. This expression-based inspection has been
implemented for Version.Requirement
, MapSet
, and Date.Range
.
Finally, we have improved the Inspect
protocol for structs so that
fields are inspected in the order they are declared in defstruct
.
The option :optional
has also been added when deriving the Inspect
protocol, giving developers more control over the struct representation.
See the updated documentation for Inspect
for a general rundown on
the approaches and options available.
<%!-- --%>
EEx.tokenize/2
Access.slice/1
Application.compile_env/4
and Application.compile_env!/3
to read the compile-time environment inside macrosDateTime.from_iso8601/2
day
/hour
/minute
on add
/diff
across different calendar modules:normalize_bitstring_modifiers
to Code.format_string!/2
Code.compile_string/2
and Code.compile_quoted/2
Code.env_for_eval/1
and Code.eval_quoted_with_env/3
__MODULE__
in several functionsEnum.slice/2
dereference_symlinks: true
in File.cp/3
and File.cp_r/3
1.0e16
and the fractional value is precisely zeroFloat.min_finite/0
and Float.max_finite/0
Inspect
protocol:optional
when deriving the Inspect protocol for hiding fields that match their default valuedefstruct
Date.Range
, MapSet
, and Version.Requirement
Macro.Env
and keywords as stacktrace definitions in IO.warn/2
IO.ANSI.syntax_colors/0
and related configuration to be shared across IEx and dbg
dbg/0-2
macro..
as a nullary operator that returns 0..-1//1
binary_slice/2
and binary_slice/3
generated: true
annotations on macro expansionKeyword.from_keys/2
and Keyword.replace_lazy/3
List.keysort/3
with support for a sorter
functionMacro.classify_atom/1
and Macro.inspect_atom/2
Macro.expand_literal/2
and Macro.path/2
Macro.Env.prune_compile_info/1
Map.from_keys/2
and Map.replace_lazy/3
MapSet.filter/2
, MapSet.reject/2
, and MapSet.symmetric_difference/2
Node.spawn_monitor/2
and Node.spawn_monitor/4
@after_verify
attribute for executing code whenever a module is verifiedPartitionSupervisor
that starts multiple isolated partitions of the same child for scalabilityPath.safe_relative/1
and Path.safe_relative_to/2
Registry.count_select/2
Stream.duplicate/2
and Stream.transform/5
String.replace/3
, String.split/3
, and String.splitter/3
String.slice/2
:zip_input_on_exit
option to Task.async_stream/3
:mfa
in the Task
struct for reflection purposesURI.append_query/2
Version.to_string/1
Version.Requirement
source in the Inspect
protocolExUnit.Callbacks.start_link_supervised!/2
ExUnit.run/1
to rerun test modules--dot-iex
line by line::
inside <<...>>
)pid/1
h/1
Logger.put_process_level/2
:config_path
and :lockfile
options to Mix.install/2
--no-optional-deps
to skip optional dependencies to test compilation works without optional dependenciesMix.Dep.Converger
now tells which deps formed a cycle--app
option to restrict recursive tasks in umbrella projects+
as a task separator instead of commamix format -
when reading from stdinmix format
plugins are missing:runtime_config_path
accept false
to skip the config/runtime.exs
:test_elixirc_options
and default to not generating docs nor debug info chunk for tests--group
flag in mix xref graph
Calendar.strftime/3
--rpc-eval
usage__exception__
field as true
when expanding exceptions in typespecsTrue
, False
, and Nil
aliases are used@derive
attributesdefimpl :for
Regex.compile/2
System.fetch_env!/1
to mirror map operationstmp_dir
in ExUnit to avoid test name collisioncapture_log
)ExUnit.after_suite/1
callback even when no tests runsetup
with imported function from within describe
failed to compileexports/1
in IEx for long function names--warnings-as-errors
when used with --all-warnings
mix format
RELEASE_MODE
after env.{sh,bat}
are executedmix xref trace
runtime: false
on Mix.install/2
File.cp/3
and File.cp_r/3
is deprecated.
Instead pass the callback the :on_conflict
key of a keyword list<%# ... %>
for comments is deprecated. Please use <% # ... %>
or the new multi-line comments with <%!-- ... --%>
Logger.enable/1
and Logger.disable/1
in favor of Logger.put_process_level/2
--app
option in mix cmd CMD
is deprecated in favor of the more efficient mix do --app app cmd CMD
Application.get_env/3
and friends in the module body is now discouraged, use Application.compile_env/3
insteaduse Bitwise
is deprecated, use import Bitwise
instead~~~
is deprecated in favor of bnot
for clarity:each_cycle
is deprecated, return a {:compile | :runtime, modules, warnings}
tuple instead<|>
to avoid ambiguity with upcoming extended numerical operatorsString.starts_with?/2
$levelpad
on message formattingMix.Tasks.Xref.calls/1
is deprecated in favor of compilation tracersThis release has been verified to work with Erlang/OTP 25 RC2.
require
Registry
send work with named tripletsMacro.to_string/1
when the plain alias Elixir
is givenString.split_at/2
.ex
and .exs
filesCode.Fragment.container_cursor_to_quoted/2
:uniq
was used inside another comprehension with :uniq
env.context_modules
is properly set inside optimized defmodule
Keyword.map/2
as it is equivalent to Keyword.new/2
Map.map/2
as it is equivalent to Map.new/2
SyntaxError
and TokenMissingError
if line is emptyArgumentError
for improper lists on apply/3
line_length
for Macro.to_string/1
Macro.to_string/1
slice
with negative positions in rangesURI.parse/1
as URI.new/1
is too strict in many common casesURI.new/1
returns nil for empty paths--version
flag halts IExMix.install/2
cacheAnnouncement: https://elixir-lang.org/blog/2021/12/03/elixir-v1-13-0-released/
:parser_options
to EEx functionsc:Calendar.year_of_era/3
to support calendars where the beginning of a new era does not align with the beginning of a new year--short-version
on the CLI that does not boot the VMCode.string_to_quoted_with_comments/2
and Code.quoted_to_algebra/2
:token_metadata
to aliases and remote calls when parsing stringsCode.Fragment
module to provide best-effort information from code fragments. The module currently provides an updated Code.Fragment.cursor_context/2
with operator support and Code.Fragment.surround_context/2
which looks at a given position in a fragment and find its surrounding delimitersCode.format_string!/2
{:on_module, bytecode, :none}
trace to compilation tracersEnum.concat/1
for lists of listsEnum.slide/3
Inspect.Opts.default_inspect_fun/1
:eof
to be given as limit to IO.getn/2
:sigils
option in import Mod, only: :sigils
and allow the sigil modifiers to be also digitsget_in
consistently abort when nil
values are found?\
is used and there is no need for a escape character**/2
)Keyword.validate/2
Keyword.filter/2
and Keyword.map/2
List.keyfind!/3
Macro.prewalker/1
and Macro.postwalker/1
required?/2
, lookup_import/2
, fetch_alias/2
, and fetch_macro_alias/2
Map.filter/2
and Map.map/2
:nillify_clauses
in Module.get_definition/3
Module.attributes_in/1
and Module.overridables_in/1
OptionParser.ParseError
messages@__records__
Task.completed/1
Task.ignore/1
to keep a task running but ignoring all of its resultsTask.async*
functionsURI.new/1
and URI.new!/1
with_io/3
to return result with captured iowith_log/2
to return result with captured logs"./
or "/
or "DRIVER:
where DRIVER
is a single letterr/1
Logger.put_application_level/2
MIX_INSTALL_FORCE
environment variable support:config
and :system_env
in Mix.install/2
Mix.installed?/0
:default
option to Mix.Shell.yes?
loadconfig
before building archivemix.exs
changes, instead recompile only files using Mix.Project
or trigger a recompilation if a compiler option changes:subdir
option to git depsloadconfig
before building escript:plugins
in mix format
that can hook into custom extensions and sigilsMix.Tasks.Format.formatter_for_file/2
sub_dirs
in Rebar 2 to help migration towards Rebar 3--if-missing
option when installing RebarREBAR_PROFILE=prod
when compiling Rebar dependencies--profile-require=time
to profile the time loading test files themselvesMIX_TEST_PARTITION
when partitions set to 1mix xref graph
trace
subcommand to print compilation dependencies between files--fail-above
option to mix xref
--label compile-connected
to mix xref
->
is followed by newlinecompile_env
Application.compile_env
or Application.compile_env!
are called without a require:static_atoms_encoder
in Code.string_to_quoted/2
also applies to quoted keyword keys{binding, nil}
in eval operations__CALLER__
or __ENV__
or __STACKTRACE__
are used in matchbyte_size
from binary concat:as
unquote_splicing
inside %{...}
without parens.
as a keyword list keywith
and for
special formsquote
with unquote
of remote calls would emit invalid AST metadatadefdelegate
to the list of unallowed macros inside protocols as protocols do not allow function definitions@callback
, @macrocallback
and @optional_callbacks
are defined inside protocoland
/or
in version requirementsExUnit.run/0
to consider all tests in a module whenever if a module's setup_all
fails.iex.exs
context after a pry session:compile_time_purge_matching
configuration@external_resource
is deletedCompiling N files (.ex)
show up multiple times if necessaryMIX_DEPS_PATH
for install commandsRELEASE_COOKIE
is emptycase
, cond
, and receive
branches where the right side is a literalCode.eval_quoted/3
and Code.eval_string/3
, such as :aliases
and :tracers
, have been deprecated in favor of passing an environment:all
on IO.getn
is deprecated in favor of :eof
URI.parse/1
is deprecated in favor of URI.new/1
and URI.new!/1
Mix.Tasks.Format.formatter_opts_for_file/2
is deprecated in favor of Mix.Tasks.Format.formatter_for_file/2
Code.cursor_context/2
is deprecated, use Code.Fragment.cursor_context/2
insteadMacro.to_string/2
is deprecated, use Macro.to_string/1
insteadSystem.get_pid/0
is deprecated, use System.pid/0
instead!
or !=
in version requirements is deprecated, use ~>
or >=
instead:strip_beam
option is deprecated in favor of :strip_beams
:exit_code
in Mix.raise/2
has been deprecated in favor of :exit_status
Mix.Config
is deprecated in favor of Config
module