Skip to content

Commit f2e78d3

Browse files
author
José Valim
committed
function(:is_atom, 1) is deprecated in favor of function(is_atom/1)
This is required so we can identity the proper function/arity from quoting and properly hygienize function retrievals.
1 parent 5d1d3f2 commit f2e78d3

File tree

5 files changed

+41
-65
lines changed

5 files changed

+41
-65
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
* deprecations
2323
* [Kernel] `setelem/3` is deprecated in favor of `set_elem/3`
24+
* [Kernel] `function(:is_atom, 1)` is deprecated in favor of `function(is_atom/1)`
2425

2526
* backwards incompatible changes
2627
* [Kernel] `unquote` now only applies to the closest quote. If your code contains a quote that contains another quote that calls unquote, it will no longer work. Use `Macro.escape` instead and pass your quoted contents up in steps, for example:

lib/elixir/include/elixir.hrl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@
2929
aliases, %% an orddict with aliases by new -> old names
3030
file, %% the current scope filename
3131
requires, %% a set with modules required
32-
macros, %% a list with macros imported by module
33-
functions}). %% a list with functions imported by module
32+
macro_macros, %% a list with macros imported from module inside a macro
33+
macros, %% a list with macros imported from module
34+
macro_functions, %% a list with functions imported from module inside a macro
35+
functions %% a list with functions imported from module
36+
}).
3437

3538
-record(elixir_quote, {
3639
line=0,

lib/elixir/lib/kernel.ex

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,9 +2089,10 @@ defmodule Kernel do
20892089
end
20902090
20912091
@doc """
2092-
Returns an anonymous function based on the given arguments.
2092+
Construct an anonymous function based on the given expression
2093+
or retrieve an existing one.
20932094
2094-
## Examples
2095+
## Function composition
20952096
20962097
iex> sum = function do
20972098
...> (x, y) -> x + y
@@ -2112,6 +2113,26 @@ defmodule Kernel do
21122113
...> sum.(1, 2)
21132114
3
21142115
2116+
All clauses must expect the same number of arguments.
2117+
2118+
## Function retrieval
2119+
2120+
The `function` macro can also be used to retrieve local, imported
2121+
and remote functions.
2122+
2123+
# Retrieve local/import
2124+
iex> f = function(is_atom/1)
2125+
iex> f.(:foo)
2126+
true
2127+
2128+
# Retrieve remote
2129+
iex> f = function(Kernel.is_atom/1)
2130+
iex> f.(:foo)
2131+
true
2132+
2133+
In case a function needs to be dynamically retrieved based on its
2134+
module, name or arity, use `function/3` instead.
2135+
21152136
## Shortcut syntax
21162137
21172138
In order to reduce verbosity, functions in Elixir can be written
@@ -2151,54 +2172,14 @@ defmodule Kernel do
21512172
defmacro function(args)
21522173
21532174
@doc """
2154-
Retrieves a local or an imported function.
2155-
2156-
## Examples
2157-
2158-
iex> f = function(:is_atom, 1)
2159-
...> f.(:foo)
2160-
true
2161-
2162-
Notice that local functions cannot be retrieved dynamically,
2163-
the following, for example, wouldn't work:
2164-
2165-
some_fun = :is_atom
2166-
function(some_fun, 1)
2167-
2168-
In such cases, one should use `function/3`:
2169-
2170-
some_fun = :is_atom
2171-
function(SomeModule, some_fun, 1)
2172-
2173-
## Shortcut syntax
2174-
2175-
One can use a shortcut syntax to retrieve such functions,
2176-
that resembles Erlang's `fun`:
2177-
2178-
f = function(is_atom/1)
2179-
f.(:foo)
2180-
2181-
"""
2182-
defmacro function(function, arity)
2183-
2184-
@doc """
2185-
Retrieves a function from a module.
2175+
Retrieves a function with given name and arity from a module.
21862176
21872177
## Examples
21882178
21892179
iex> f = function(Kernel, :is_atom, 1)
21902180
...> f.(:foo)
21912181
true
21922182
2193-
## Shortcut syntax
2194-
2195-
One can use a shortcut syntax to retrieve such functions,
2196-
that resembles Erlang's `fun`:
2197-
2198-
iex> f = function(Kernel.is_atom/1)
2199-
...> f.(:foo)
2200-
true
2201-
22022183
"""
22032184
defmacro function(module, function, arity)
22042185

lib/elixir/src/elixir_macros.erl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,16 @@ translate({ function, Meta, [[{do,{ '->',_,Pairs}}]] }, S) ->
5656
assert_no_match_or_guard_scope(Meta, 'function', S),
5757
elixir_translator:translate_fn(Meta, Pairs, S);
5858

59-
translate({ function, Meta, [{ '/', _, [{{ '.', _ ,[M, F] }, _ , [] }, A]}] }, S) ->
59+
translate({ function, Meta, [{ '/', _, [{{ '.', _ ,[M, F] }, _ , [] }, A]}] }, S) when is_atom(F), is_integer(A) ->
6060
translate({ function, Meta, [M, F, A] }, S);
6161

62-
translate({ function, Meta, [{ '/', _, [{F, _, Q}, A]}] }, S) when is_atom(Q) ->
63-
translate({ function, Meta, [F, A] }, S);
62+
translate({ function, Meta, [{ '/', _, [{F, _, C}, A]}] }, S) when is_atom(F), is_integer(A), is_atom(C) ->
63+
assert_no_match_or_guard_scope(Meta, 'function', S),
64+
65+
case elixir_dispatch:import_function(Meta, F, A, S) of
66+
false -> syntax_error(Meta, S#elixir_scope.file, "cannot convert a macro to a function");
67+
Else -> Else
68+
end;
6469

6570
translate({ function, Meta, [_] }, S) ->
6671
assert_no_match_or_guard_scope(Meta, 'function', S),
@@ -71,6 +76,7 @@ translate({ function, Meta, [_, _] = Args }, S) ->
7176

7277
case translate_args(Args, S) of
7378
{ [{atom,_,Name}, {integer,_,Arity}], SA } ->
79+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "function(:~ts, ~B) is deprecated, please use function(~ts/~B)", [Name, Arity, Name, Arity]),
7480
case elixir_dispatch:import_function(Meta, Name, Arity, SA) of
7581
false -> syntax_error(Meta, S#elixir_scope.file, "cannot convert a macro to a function");
7682
Else -> Else

lib/elixir/test/elixir/kernel_test.exs

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -141,41 +141,26 @@ defmodule KernelTest do
141141
defmodule Function do
142142
use ExUnit.Case, async: true
143143

144-
test :retrieve_remote_function do
145-
assert is_function(function(:erlang, :atom_to_list, 1))
146-
assert :erlang.fun_info(function(:erlang, :atom_to_list, 1), :arity) == {:arity, 1}
147-
assert function(:erlang, :atom_to_list, 1).(:a) == 'a'
148-
end
149-
150144
test :remote_syntax_function do
145+
assert function(:erlang, :atom_to_list, 1).(:a) == 'a'
151146
assert function(:erlang.atom_to_list/1) ==
152147
function(:erlang, :atom_to_list, 1)
153148
assert function(Enum.map/2) == function(Enum, :map, 2)
154149
end
155150

156-
test :retrieve_local_function do
157-
assert is_function(function(:atl, 1))
158-
assert :erlang.fun_info(function(:atl, 1), :arity) == {:arity, 1}
159-
assert function(:atl, 1).(:a) == 'a'
160-
end
161-
162151
test :local_syntax_function do
163152
assert function(atl/1).(:a) == 'a'
164153
end
165154

166155
test :retrieve_imported_function do
167-
assert is_function(function(:atom_to_list, 1))
168-
assert :erlang.fun_info(function(:atom_to_list, 1), :arity) == {:arity, 1}
169-
assert function(:atom_to_list, 1).(:a) == 'a'
156+
assert function(atom_to_list/1).(:a) == 'a'
170157
end
171158

172159
test :retrieve_dynamic_function do
173160
a = :erlang
174161
b = :atom_to_list
175162
c = 1
176163

177-
assert is_function(function(a, b, c))
178-
assert :erlang.fun_info(function(a, b, c), :arity) == {:arity, 1}
179164
assert function(a, b, c).(:a) == 'a'
180165
end
181166

0 commit comments

Comments
 (0)