Skip to content

Commit 0b640d9

Browse files
author
José Valim
committed
Document the dot as a special form
We include common examples, quoted expressions, and more.
1 parent b161d80 commit 0b640d9

File tree

1 file changed

+142
-21
lines changed

1 file changed

+142
-21
lines changed

lib/elixir/lib/kernel/special_forms.ex

Lines changed: 142 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,120 @@ defmodule Kernel.SpecialForms do
179179
defmacro :<<>>.(args)
180180

181181
@doc """
182-
`alias` is used to setup atom aliases, often useful with modules names.
182+
Defines a remote call or an alias.
183+
184+
The dot (`.`) in Elixir can be used for remote calls:
185+
186+
iex> String.downcase("FOO")
187+
"foo"
188+
189+
In this example above, we have used `.` to invoke `downcase` in the
190+
`String` alias, passing "FOO" as argument. We can also use the dot
191+
for creating aliases:
192+
193+
iex> Hello.World
194+
Hello.World
195+
196+
This time, we have joined two aliases, defining the final alias
197+
`Hello.World`.
198+
199+
## Syntax
200+
201+
The right side of `.` may be a word starting in upcase, which represents
202+
an alias, a word starting with lowercase or underscore, any valid language
203+
operator or any name wrapped in single- or double-quotes. Those are all valid
204+
examples:
205+
206+
iex> Kernel.Sample
207+
Kernel.Sample
208+
iex> Kernel.length([1,2,3])
209+
3
210+
iex> Kernel.+(1, 2)
211+
3
212+
iex> Kernel."length"([1,2,3])
213+
3
214+
iex> Kernel.'+'(1, 2)
215+
3
216+
217+
Note that `Kernel."HELLO"` will be treated as a remote call and not an alias.
218+
This choice was done so every time single- or double-quotes are used, we have
219+
a remote call irregardless of the quote contents. This decision is also reflected
220+
in the quoted expressions discussed below.
221+
222+
## Runtime (dynamic) behaviour
223+
224+
The result returned by `.` is always specified by the right-side:
225+
226+
iex> x = String
227+
iex> x.downcase("FOO")
228+
"foo"
229+
iex> x.Sample
230+
String.Sample
231+
232+
In case the right-side is also dynamic, `.`'s behaviour can be reproduced
233+
at runtime via `apply/3` and `Module.concat/2`:
234+
235+
iex> apply(Kernel, :+, [1,2])
236+
3
237+
iex> Module.concat(Kernel, Sample)
238+
Kernel.Sample
239+
240+
## Quoted expression
241+
242+
When `.` is used, the quoted expression may take two distinct
243+
forms. When the right side starts with a lowercase letter (or
244+
underscore):
245+
246+
iex> quote do: String.downcase("FOO")
247+
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
248+
249+
Notice we have an inner tuple, containing the atom `:.` representing
250+
the dot as first element:
251+
252+
{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}
253+
254+
This tuple follows the general quoted expression structure in Elixir,
255+
with the name as first argument, some keyword list as metadata as second,
256+
and the number of arguments as third. In this case, the arguments is the
257+
alias `String` and the atom `:downcase`. The second argument is **always**
258+
an atom:
259+
260+
iex> quote do: String."downcase"("FOO")
261+
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
262+
263+
The tuple containing `:.` is wrapped in another tuple, which actually
264+
represents the function call, and has `"FOO"` as argument.
265+
266+
When the right side is an alias (i.e. starts with uppercase), we get instead:
267+
268+
iex> quote do: Hello.World
269+
{:__aliases__, [alias: false], [:Hello, :World]}
270+
271+
We got into more details about aliases in the `__aliases__` special form
272+
documentation.
273+
274+
## Unquoting
275+
276+
We can also use unquote to generate a remote call in a quoted expression:
277+
278+
iex> x = :downcase
279+
iex> quote do: String.unquote(x)("FOO")
280+
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
281+
282+
Similar to `Kernel."HELLO"`, `unquote(x)` will always generate a remote call,
283+
independent of the value of `x`. To generate an alias via the quoted expression,
284+
one needs to rely on `Module.concat/2`:
285+
286+
iex> x = Sample
287+
iex> quote do: Module.concat(String, unquote(x))
288+
{{:., [], [{:__aliases__, [alias: false], [:Module]}, :concat]}, [],
289+
[{:__aliases__, [alias: false], [:String]}, Sample]}
290+
291+
"""
292+
defmacro (:.).(left, right)
293+
294+
@doc """
295+
`alias` is used to setup aliases, often useful with modules names.
183296
184297
## Examples
185298
@@ -217,8 +330,7 @@ defmodule Kernel.SpecialForms do
217330
defmacro alias(module, opts)
218331

219332
@doc """
220-
`require` is used to require the presence of external
221-
modules so macros can be invoked.
333+
Requires a given module to be compiled and loaded.
222334
223335
## Examples
224336
@@ -246,6 +358,8 @@ defmodule Kernel.SpecialForms do
246358
defmacro require(module, opts)
247359

248360
@doc """
361+
Imports function and macros from other modules.
362+
249363
`import` allows one to easily access functions or macros from
250364
others modules without using the qualified name.
251365
@@ -335,21 +449,24 @@ defmodule Kernel.SpecialForms do
335449
defmacro import(module, opts)
336450

337451
@doc """
338-
Returns the current environment information as a `Macro.Env`
339-
record. In the environment you can access the current filename,
452+
Returns the current environment information as a `Macro.Env[]` record.
453+
454+
In the environment you can access the current filename,
340455
line numbers, set up aliases, the current function and others.
341456
"""
342457
defmacro __ENV__
343458

344459
@doc """
345460
Returns the current module name as an atom or `nil` otherwise.
461+
346462
Although the module can be accessed in the `__ENV__`, this macro
347463
is a convenient shortcut.
348464
"""
349465
defmacro __MODULE__
350466

351467
@doc """
352468
Returns the current file name as a binary.
469+
353470
Although the file can be accessed in the `__ENV__`, this macro
354471
is a convenient shortcut.
355472
"""
@@ -840,10 +957,11 @@ defmodule Kernel.SpecialForms do
840957
defmacro lc(args)
841958

842959
@doc """
843-
Defines a bit comprehension. It follows the same syntax and
844-
behaviour as a list comprehension but expects each element
845-
returned to be a bitstring. For example, here is how to remove
846-
all spaces from a string:
960+
Defines a bit comprehension.
961+
962+
It follows the same syntax and behaviour as a list comprehension
963+
but expects each element returned to be a bitstring. For example,
964+
here is how to remove all spaces from a string:
847965
848966
iex> bc <<c>> inbits " hello world ", c != ? , do: <<c>>
849967
"helloworld"
@@ -852,6 +970,8 @@ defmodule Kernel.SpecialForms do
852970
defmacro bc(args)
853971

854972
@doc """
973+
Internal special form for block expressions.
974+
855975
This is the special form used whenever we have a block
856976
of expressions in Elixir. This special form is private
857977
and should not be invoked directly:
@@ -863,7 +983,7 @@ defmodule Kernel.SpecialForms do
863983
defmacro __block__(args)
864984

865985
@doc """
866-
Captures a call as an anonymous function.
986+
Captures an anonymous function.
867987
868988
The most common format to capture a function is via module,
869989
name and arity:
@@ -941,30 +1061,31 @@ defmodule Kernel.SpecialForms do
9411061
defmacro unquote(name)(expr)
9421062

9431063
@doc """
944-
This is the special form used whenever we have to temporarily
945-
change the scope information of a block. Used when `quote` is
946-
invoked with `location: :keep` to execute a given block as if
947-
it belonged to another file.
1064+
Internal special form for modifying scope information.
1065+
1066+
Used when `quote` is invoked with `location: :keep` to execute
1067+
a given block as if it belonged to another file.
9481068
9491069
quote location: :keep, do: 1
9501070
#=> { :__scope__, [line: 1], [[file: "iex"],[do: 1]] }
9511071
952-
Check `quote/1` for more information.
1072+
Check `quote/2` for more information.
9531073
"""
9541074
defmacro __scope__(opts, args)
9551075

9561076
@doc """
957-
This is the special form used to hold aliases information.
1077+
Internal special form to hold aliases information.
1078+
9581079
It is usually compiled to an atom:
9591080
960-
quote do: Foo.Bar #=>
961-
{ :__aliases__, [], [:Foo,:Bar] }
1081+
iex> quote do: Foo.Bar
1082+
{:__aliases__, [alias: false], [:Foo, :Bar]}
9621083
9631084
Elixir represents `Foo.Bar` as `__aliases__` so calls can be
9641085
unambiguously identified by the operator `:.`. For example:
9651086
966-
quote do: Foo.bar #=>
967-
{{:.,[],[{:__aliases__,[],[:Foo]},:bar]},[],[]}
1087+
iex> quote do: Foo.bar
1088+
{{:., [], [{:__aliases__, [alias: false], [:Foo]}, :bar]}, [], []}
9681089
9691090
Whenever an expression iterator sees a `:.` as the tuple key,
9701091
it can be sure that it represents a call and the second argument
@@ -981,7 +1102,7 @@ defmodule Kernel.SpecialForms do
9811102
4) When the head element of aliases is not an atom, it is expanded at runtime:
9821103
9831104
quote do: some_var.Foo
984-
{:__aliases__,[],[{:some_var,[],:quoted},:Bar]}
1105+
{:__aliases__, [], [{:some_var, [], Elixir}, :Foo]}
9851106
9861107
Since `some_var` is not available at compilation time, the compiler
9871108
expands such expression to:

0 commit comments

Comments
 (0)