Skip to content

Commit

Permalink
Document tuples inside containers, closes #13681
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Jun 22, 2024
1 parent 2eb85ea commit f1f54df
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 9 deletions.
21 changes: 16 additions & 5 deletions lib/elixir/pages/references/syntax-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Data structures such as lists, tuples, and binaries are marked respectively by t
Maps use the `%{...}` notation and each key-value is given by pairs marked with `=>`, such as `%{"hello" => 1, 2 => "world"}`.
Both keyword lists (list of two-element tuples where the first element is atom) and maps with atom keys support a keyword notation where the colon character `:` is moved to the end of the atom. `%{hello: "world"}` is equivalent to `%{:hello => "world"}` and `[foo: :bar]` is equivalent to `[{:foo, :bar}]`. This notation is a syntax sugar that emits the same AST representation. It will be explained in later sections.
Both keyword lists (list of two-element tuples where the first element is atom) and maps with atom keys support a keyword notation where the colon character `:` is moved to the end of the atom. `%{hello: "world"}` is equivalent to `%{:hello => "world"}` and `[foo: :bar]` is equivalent to `[{:foo, :bar}]`. We discuss keywords in later sections.
### Structs
Expand Down Expand Up @@ -460,15 +460,19 @@ However, Elixir introduces a syntax sugar where the keywords above may be writte
[foo: 1, bar: 2]
```
Atoms with foreign characters, such as whitespace, must be wrapped in quotes. This rule applies to keywords as well:
In order to be valid keyword syntax, `:` cannot be preceded by any whitespace (`foo : 1` is invalid) and has to be followed by whitespace (`foo:1` is invalid). Atoms with foreign characters, such as whitespace, must be wrapped in quotes. This rule applies to keywords as well:
```elixir
[{:"foo bar", 1}, {:"bar baz", 2}] == ["foo bar": 1, "bar baz": 2]
["foo bar": 1, "bar baz": 2] == [{:"foo bar", 1}, {:"bar baz", 2}]
```
Remember that, because lists and two-element tuples are quoted literals, by definition keywords are also literals (in fact, the only reason tuples with two elements are quoted literals is to support keywords as literals).
You can also mix regular list elements with keywords, but keywords must come last:
In order to be valid keyword syntax, `:` cannot be preceded by any whitespace (`foo : 1` is invalid) and has to be followed by whitespace (`foo:1` is invalid).
```elixir
[:foo, :bar, baz: :bat] == [:foo, :bar, {:baz, :bat}]
```
Finally, because lists and two-element tuples are quoted literals, by definition keywords are also literals.
### Keywords as last arguments
Expand All @@ -490,6 +494,13 @@ which, as per the previous section, is the same as
if(condition, [{:do, this}, {:else, that}])
```
This same notation is available inside containers (such as `{...}`, `%{...}`, etc) as well:
```elixir
{:foo, :bar, baz: :bat} == {:foo, :bar, {:baz, :bat}}

This comment has been minimized.

Copy link
@michalmuskala

michalmuskala Jun 23, 2024

Member

This is not correct. The value is:

iex(1)> {:foo, :bar, baz: :bat}
{:foo, :bar, [baz: :bat]}

This comment has been minimized.

Copy link
@josevalim

josevalim Jun 23, 2024

Author Member

Thank you, it has been fixed in main!

%{:foo => :bar, baz: :bat} == %{:foo => :bar, :baz => :bat}}
```
### `do`-`end` blocks
The last syntax convenience are `do`-`end` blocks. `do`-`end` blocks are equivalent to keywords as the last argument of a function call, where the block contents are wrapped in parentheses. For example:
Expand Down
8 changes: 4 additions & 4 deletions lib/elixir/src/elixir_parser.yrl
Original file line number Diff line number Diff line change
Expand Up @@ -591,13 +591,13 @@ list -> open_bracket list_args close_bracket : build_list('$1', '$2', '$3').
% Tuple

tuple -> open_curly '}' : build_tuple('$1', [], '$2').
tuple -> open_curly kw_data '}' : bad_keyword('$1', tuple).
tuple -> open_curly kw_data '}' : bad_keyword('$1', tuple, "'{'").
tuple -> open_curly container_args close_curly : build_tuple('$1', '$2', '$3').

% Bitstrings

bitstring -> open_bit '>>' : build_bit('$1', [], '$2').
bitstring -> open_bit kw_data '>>' : bad_keyword('$1', bitstring).
bitstring -> open_bit kw_data '>>' : bad_keyword('$1', bitstring, "'<<'").
bitstring -> open_bit container_args close_bit : build_bit('$1', '$2', '$3').

% Map and structs
Expand Down Expand Up @@ -1118,11 +1118,11 @@ error_bad_atom(Token) ->
"If the '.' was meant to be part of the atom's name, "
"the atom name must be quoted. Syntax error before: ", "'.'").

bad_keyword(Token, Context) ->
bad_keyword(Token, Context, StartString) ->
return_error(?location(Token),
"unexpected keyword list inside " ++ atom_to_list(Context) ++ ". "
"Did you mean to write a map (using %{...}) or a list (using [...]) instead? "
"Syntax error after: ", "'{'").
"Syntax error after: ", StartString).

maybe_bad_keyword_call_follow_up(_Token, KW, {'__cursor__', _, []} = Expr) ->
reverse([Expr | KW]);
Expand Down

0 comments on commit f1f54df

Please sign in to comment.