Skip to content

Commit 4eb9cd3

Browse files
author
José Valim
committed
Improve docs and error messages
1 parent bf9e190 commit 4eb9cd3

File tree

2 files changed

+101
-33
lines changed

2 files changed

+101
-33
lines changed

lib/elixir/lib/kernel/special_forms.ex

Lines changed: 100 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -484,8 +484,8 @@ defmodule Kernel.SpecialForms do
484484
"""
485485
defmacro __DIR__
486486

487-
@doc """
488-
Allows you to get the representation of any expression.
487+
@doc %S"""
488+
Gets the representation of any expression.
489489
490490
## Examples
491491
@@ -522,7 +522,7 @@ defmodule Kernel.SpecialForms do
522522
* `:bind_quoted` - Passes a binding to the macro. Whenever a binding is given,
523523
`unquote` is automatically disabled;
524524
525-
## Macro literals
525+
## Quote literals
526526
527527
Besides the tuple described above, Elixir has a few literals that
528528
when quoted return themselves. They are:
@@ -531,43 +531,111 @@ defmodule Kernel.SpecialForms do
531531
1 #=> Integers
532532
2.0 #=> Floats
533533
[1, 2] #=> Lists
534-
"binaries" #=> Binaries
534+
"strings" #=> Strings
535535
{key, value} #=> Tuple with two elements
536536
537-
## Hygiene and context
537+
## Quote and macros
538+
539+
`quote` is commonly used with macros for code generation. As an exercise,
540+
let's define a macro that multiplies a number by itself (squared). Note
541+
there is no reason to define such as a macro (and it would actually be
542+
seen as a bad practice), but it is simple enough that allows us to focus
543+
on the important aspects of quotes and macros:
544+
545+
defmodule Math do
546+
defmacro squared(x) do
547+
quote do
548+
unquote(x) * unquote(x)
549+
end
550+
end
551+
end
552+
553+
We can invoke it as:
554+
555+
import Math
556+
IO.puts "Got #{squared(5)}"
557+
558+
At first, there is nothing in this example that actually reveals it is a
559+
macro. But what is happening is that, at compilation time, `squared(5)`
560+
becomes `5 * 5`. We can see this behaviour in practice though
561+
because our macro actually has a bug:
562+
563+
import Math
564+
my_number = fn ->
565+
IO.puts "Returning 5"
566+
5
567+
end
568+
IO.puts "Got #{squared(my_number.())}"
569+
570+
The example above will print:
571+
572+
Returning 5
573+
Returning 5
574+
25
575+
576+
Notice how "Returning 5" was printed twice, instead of just once. This is
577+
because a macro receives an expression and not a value (which is what we
578+
would expect in a regular function). This means that:
538579
539-
Elixir macros are hygienic by means of deferred resolution.
540-
This means variables, aliases and imports defined inside the
541-
quoted refer to the context that defined the macro and not
542-
the context where the macro is expanded.
580+
squared(my_number.())
543581
544-
For this mechanism to work, every quoted code is attached
545-
to a context. Consider the following example:
582+
Actually expands to:
546583
547-
defmodule ContextSample do
548-
def hello do
549-
quote do: world
584+
my_number.() * my_number.()
585+
586+
Which invokes the function twice, explaining why we get the printed value
587+
twice! In the majority of the cases, this is actually unexpected behaviour,
588+
and that's why one of the first things you need to keep in mind when it
589+
comes to macros is to **not unquote the same value more than once**.
590+
591+
Let's fix our macro:
592+
593+
defmodule Math do
594+
defmacro squared(x) do
595+
quote do
596+
x = unquote(x)
597+
x * x
598+
end
599+
end
600+
end
601+
602+
Now invoking `square(my_number.())` as before will print the value just
603+
once.
604+
605+
In fact, this pattern is so common that most of the times you will want
606+
to use the `bind_quoted` option with `quote`:
607+
608+
defmodule Math do
609+
defmacro squared(x) do
610+
quote bind_quoted: [x: x] do
611+
x * x
612+
end
550613
end
551614
end
552615
553-
ContextSample.hello
554-
#=> {:world,[],ContextSample}
616+
`:bind_quoted` will translate to the same example as above. `:bind_quoted`
617+
can be used in many cases and is seen as good practice, not only because
618+
it helps us from running into common mistakes but also because it allows
619+
us to leverage other tools exposed by macros, as unquote fragments discussed
620+
in some sections below.
621+
622+
Before we finish this brief introduction, you will notice that, even though
623+
we defined a variable `x` inside our quote:
624+
625+
quote do
626+
x = unquote(x)
627+
x * x
628+
end
555629
556-
Notice how the third element of the returned tuple is the
557-
module name. This means that the variable is associated to the
558-
`ContextSample` module and only code generated by this module
559-
will be able to access that particular `world` variable. Not
560-
only variables hygiene, but also imports and aliases hygiene
561-
are delimited by context.
630+
When we call:
562631
563-
While this means macros from the same module could have
564-
conflicting variables, it also allows different quotes from
565-
the same module to access them.
632+
import Math
633+
squared(5)
634+
x #=> ** (RuntimeError) undefined function or variable: x
566635
567-
The context can be disabled or changed by explicitly setting
568-
the `context` option. All hygiene mechanisms are based on such
569-
context and we are going to explore each of them in the following
570-
subsections.
636+
We can see that `x` did not leak to the user context. This happens
637+
because Elixir macros are hygienic, a topic we will discuss at length
638+
in the next sections as well.
571639
572640
### Hygiene in variables
573641
@@ -622,7 +690,7 @@ defmodule Kernel.SpecialForms do
622690
623691
Hygiene.write
624692
Hygiene.read
625-
#=> no variable a
693+
#=> ** (RuntimeError) undefined function or variable: a
626694
627695
For such, you can explicitly pass the current module scope as
628696
argument:
@@ -839,8 +907,8 @@ defmodule Kernel.SpecialForms do
839907
One solution for this problem is to disable unquoting in the
840908
macro, however, doing that would make it impossible to inject
841909
`kv` representation into the tree. That's when the `:bind_quoted`
842-
option comes to the rescue. By using `:bind_quoted`, we can
843-
automatically disable unquoting while still injecting the
910+
option comes to the rescue (again!). By using `:bind_quoted`, we
911+
can automatically disable unquoting while still injecting the
844912
desired variables into the tree:
845913
846914
defmacro defkv(kv) do

lib/mix/lib/mix/project.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ defmodule Mix.Project do
5454
:ok
5555
{ :error, other } when is_binary(other) ->
5656
raise Mix.Error, message: "Trying to load #{inspect atom} from #{inspect file}" <>
57-
" but another project with the same was already defined at #{inspect other}"
57+
" but another project with the same name was already defined at #{inspect other}"
5858
end
5959
end
6060

0 commit comments

Comments
 (0)