1. Elixir with macro <-
and =
So Elixir with macro accepts Matching clauses <-
and Bare expressions =
. Both match patterns and do not leak variables assigned inside these structures.
with {:ok, width} <- Map.fetch(%{width: 10}, :width),
do: {:ok, 2 * width}
#=> {:ok, 20}
with {:ok, width} = Map.fetch(%{width: 10}, :width),
do: {:ok, 2 * width}
#=> {:ok, 20}
So what is the difference between these?
When a match fails, matching clauses <-
returns failed result but bare expressions =
raises a MatchError
:
with {:ok, width} <- Map.fetch(%{height: 10}, :width),
do: {:ok, 2 * width}
#=> :error
with {:ok, width} = Map.fetch(%{height: 10}, :width),
do: {:ok, 2 * width}
#=> ** (MatchError) no match of right hand side value: :error
Another difference is that when
guard is only available for matching clause.
with {:ok, width} when is_number(width) <- Map.fetch(%{width: 10}, :width),
do: {:ok, 2 * width}
#=> {:ok, 20}
2. Binary pattern matching
You might be familiar with the most popular form of Elixir pattern matching involving tuples:
iex > {:ok, x} = {:ok, 1000}
{:ok, 1000}
iex > x
1000
You can also pattern match binaries:
iex > <<1, y, 2>> = <<1, 255, 2>>
<<1, 255, 2>>
iex > y
255
This gets powerful when you’re able to match binary formats.
iex > <<x :: binary-1, y :: binary-2, z :: binary>> = <<1, 255, 254, 2>>
<<1, 255, 254, 2>>
iex > x
<<1>>
iex > y
<<255, 254>>
iex > z
<<2>>
Here’s an article about using binary pattern matching to parse a png file.
3. Word Lists For Atoms
The ~w
sigil allows you to create a list of words (i.e. strings).
> ~w(one two three)
["one", "two", "three"]
It sets itself apart though with some modifiers. The default behavior
matches the s
modifier (for strings).
> ~w(one two three)s
["one", "two", "three"]
Where it gets more interesting is with the a
modifier allowing you to
create a list of atoms.
> ~w(one two three)a
[:one, :two, :three]
Note: there is a third modifier, c
, for char lists.
> ~w(one two three)c
['one', 'two', 'three']
4. Find and Open Port with Elixir
iex(1)> {:ok, port} = :gen_tcp.listen(0, []) #listen on an available port
{:ok, #Port<0.1465>}
iex(2)> {:ok, port_number} = :inet.port(port) #get the port number of that port
{:ok, 63470}
iex(3)> port_number #here is the port number!
63470
iex(4)> Port.close port #go ahead and close that port if you want
true
5. Virtual Fields With Ecto Schema
If you’d like to include a particular key-value pair in an Ecto changeset,
it needs to be included as a field in the schema. In the case of something
akin to a password field, you want to be able to perform validations against
it, but the password itself does not have a column in the database. In other
words, you want to use the password in memory as part of the validation
process but not save it to the database. To accomplish this, you need to
specify that it is a virtual
field.
schema "users" do
field :username, :string
field :password_digest, :string
field :password, :string, virtual: true
end
With that schema, you can then validate the :password
and transform it
into the corresponding :password_digest
field.
def registration_changeset(model, params) do
model
|> changeset(params) # do other standard validations
|> cast(params, [:password]) # include :password in the changeset
|> validate_length(:password, min: 8) # validations
|> put_pass_hash() # transform into :password_digest
end
Mix xref and Elixir compiler warnings
The release of Elixir v1.3 added a mix task called xref
for performing cross reference checks of your code. mix xref
gives us some handy tools!
6. Find unreachable code:
❯ mix xref unreachable
lib/exonk8s/foo.ex:16: SomeModule.non_existant/0
Find callers of modules and functions:
❯ mix xref callers SomeModule
lib/exonk8s/foo.ex:16: SomeModule.non_existant/0
Generate a dependency graph:
> mix xref graph
lib/exonk8s.ex
├── lib/exonk8s/repo.ex
└── lib/router.ex
└── lib/exonk8s/musician_controller.ex (compile)
├── lib/exonk8s/foo.ex
├── lib/exonk8s/musician.ex
└── lib/exonk8s/repo.ex
...snipped for brevity
The release of Elixir v1.6 added the module attribute @deprecated
to enable library authors to warn users of deprecated functions.
❯ mix xref deprecated
Compiling 2 files (.ex)
Foo Loaded
warning: ExOnK8s.Foo.lazy/0 is deprecated. You should be more eager.
lib/exonk8s/musician_controller.ex:10
lib/exonk8s/musician_controller.ex:10: ExOnK8s.Foo.lazy/0
Lastly I should note that xref deprecated
and xref unreachable
are included in the compiler warnings, so you you’ve likely seen these at work even if you didn’t know they were there.
7. View your outdated packages
To see which packages in a mix app need updating, you can run mix hex.outdated
$ mix hex.outdated
Dependency Current Latest Update possible
appsignal 1.3.2 1.3.3 Yes
basic_auth 2.1.4 2.1.4
cachex 2.1.0 2.1.0
...
It prints out a handy table letting you quickly view the current and latest versions and if they can be updated. If Update possible
is No
, check your semantic version lockdown of that package in mix.exs
.
mix hex.outdated
accepts a few arguments:
--all
which shows all outdated packages, including children of packages defined in mix.exs--pre
which include pre-releases when checking for newer versions (be adventurous!)
Visualize Your Elixir Dependencies
8. To visualize your Elixir dependencies, try this:
$ mix deps.tree
This prints a tree showing your dependencies (and their dependencies):
$ mix deps.tree
tilex
├── gettext ~> 0.13 (Hex package)
├── hackney 1.8.0 (Hex package)
│ ├── certifi 1.1.0 (Hex package)
│ ├── idna 4.0.0 (Hex package)
│ ├── metrics 1.0.1 (Hex package)
│ ├── mimerl 1.0.2 (Hex package)
│ └── ssl_verify_fun 1.1.1 (Hex package)
9. Phoenix will watch your JS for you, just watch!
With es6 javascript ecosystems most compilation/transpilation is done with watchers, programs that watch for changes in your code and then trigger compilation based on the changes you’ve made.
Phoenix has a method of integrating those watchers into the server itself.
config :myapp, MyApp.Endpoint,
watchers: [yarn: ["run", "watch", cd: Path.expand("../assets", __DIR__)]]
The above configuration goes in config/dev.exs
and runs the watch command whenever you start the server/endpoint. The keyword option cd
is as of yet undocumented, but does what you think it does, changes the directory to the path you configure, which by convention in Phoenix 1.3 would be the assets directory. cd
is the only keyword option.
The watchers start when you run your server. Just make a change to your javascript and refresh the page!
10. Setting breaks in IEx for debugging
With the release of Elixir 1.5 comes some handy new IEx helpers, one of which is the ability to add break points throughout your code.
You can add a break to any function using break!/2
or break!/4
:
defmodule MyModule do
def hello(name) do
"hello " <> name
end
end
iex(1)> break!(MyModule.hello/ 1)
or
iex(1)> break!(MyModule, :hello, 1)
Both break/2
and break/4
accept an additional argument for how many stops you want to make. Useful for recursive functions where you may want to stop multiple times.
To see what breaks you have use breaks/0
iex(1)> breaks()
ID Module.function/arity Pending stops
---- ----------------------- ---------------
1 MyModule.hello/1 1
Now when you call the function, you’ll be placed into the a debugger and you can inspect whats being passed in:
iex(4)> MyModule.hello("world")
Break reached: MyModule.hello/1 (lib/my_module.ex:2)
1: defmodule MyModule do
2: def hello(name) do
3: "hello " <> name
4: end
pry(1)> name
"world"
To exit the break and start a new shell process use respawn/0
pry(2)> respawn
Interactive Elixir (1.5.0) - press Ctrl+C to exit (type h() ENTER for help)
"hello world"
iex(1)>