Skip to content

Commit 6a68ca5

Browse files
author
José Valim
committed
Deprecate function/1 and function/3
1 parent b5428ad commit 6a68ca5

File tree

17 files changed

+65
-184
lines changed

17 files changed

+65
-184
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
* [OptionParser] Ensure `--no-flag` sets `:flag` to false when `:flag` is a registered boolean switch
3737

3838
* deprecations
39+
* [Kernel] `function(Mod.fun/arity)` and `function(fun/arity)` are deprecated in favor of `&Mod.fun/arity` and `&fun/arity`
40+
* [Kernel] `function/3` is deprecated in favor of `Module.function/3`
3941
* [Kernel] `Kernel.ParallelCompiler` now receives a set of callbacks instead of a single one
4042
* [Mix] `:test_coverage` option now expect keywords arguments and the `--cover` flag is now treated as a boolean
4143

lib/eex/lib/eex/tokenizer.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,11 @@ defmodule EEx.Tokenizer do
128128
end
129129

130130
defp fn_index(tokens) do
131-
Enum.find_index(tokens, function do
131+
Enum.find_index tokens, fn
132132
{ :fn_paren, _ } -> true
133133
{ :fn, _ } -> true
134134
_ -> false
135-
end)
135+
end
136136
end
137137

138138
defp end_index(tokens) do

lib/elixir/lib/kernel.ex

Lines changed: 0 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,101 +2296,6 @@ defmodule Kernel do
22962296
{ other, acc }
22972297
end
22982298

2299-
@doc """
2300-
Construct an anonymous function based on the given expression
2301-
or retrieve an existing one.
2302-
2303-
## Function composition
2304-
2305-
iex> sum = function do
2306-
...> (x, y) -> x + y
2307-
...> end
2308-
...> sum.(1, 2)
2309-
3
2310-
2311-
Notice that a function needs to be invoked using the dot between
2312-
the function and the arguments.
2313-
2314-
Multiple clauses can be specified as in `case`, `receive` and
2315-
similar macros:
2316-
2317-
iex> sum = function do
2318-
...> x, y when y > 0 -> x + y
2319-
...> x, y -> x - y
2320-
...> end
2321-
...> sum.(1, 2)
2322-
3
2323-
2324-
All clauses must expect the same number of arguments.
2325-
2326-
## Function retrieval
2327-
2328-
The `function` macro can also be used to retrieve local, imported
2329-
and remote functions.
2330-
2331-
# Retrieve local/import
2332-
iex> f = function(is_atom/1)
2333-
iex> f.(:foo)
2334-
true
2335-
2336-
# Retrieve remote
2337-
iex> f = function(Kernel.is_atom/1)
2338-
iex> f.(:foo)
2339-
true
2340-
2341-
In case a function needs to be dynamically retrieved based on its
2342-
module, name or arity, use `function/3` instead.
2343-
2344-
## Shortcut syntax
2345-
2346-
In order to reduce verbosity, functions in Elixir can be written
2347-
using a shortcut syntax via `fn`:
2348-
2349-
Enum.map [1, 2, 3], fn x ->
2350-
x * 2
2351-
end
2352-
2353-
Not only is the example shorter, it solves ambiguity issues. Since
2354-
`do/end` always matches the furthest call, if we used the `function`
2355-
macro as below:
2356-
2357-
Enum.map [1, 2, 3], function do
2358-
x -> x * 2
2359-
end
2360-
2361-
It would be parsed as:
2362-
2363-
Enum.map([1, 2, 3], function) do
2364-
x -> x * 2
2365-
end
2366-
2367-
The stab shortcut syntax has the proper precedence:
2368-
2369-
Enum.map [1, 2, 3], fn
2370-
x -> x * 2
2371-
end
2372-
2373-
Which is handled as:
2374-
2375-
Enum.map([1, 2, 3], fn
2376-
x -> x * 2
2377-
end)
2378-
2379-
"""
2380-
defmacro function(args)
2381-
2382-
@doc """
2383-
Retrieves a function with given name and arity from a module.
2384-
2385-
## Examples
2386-
2387-
iex> f = function(Kernel, :is_atom, 1)
2388-
...> f.(:foo)
2389-
true
2390-
2391-
"""
2392-
defmacro function(module, function, arity)
2393-
23942299
@doc """
23952300
Matches the given expression against the match clauses.
23962301

lib/elixir/lib/module.ex

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,19 @@ defmodule Module do
463463
:elixir_aliases.safe_concat([left, right])
464464
end
465465
466+
@doc """
467+
Gets an anonymous function from the given module, function
468+
and arity. The module and function are not verified to exist.
469+
470+
iex> fun = Module.function(Kernel, :is_atom, 1)
471+
iex> fun.(:hello)
472+
true
473+
474+
"""
475+
def function(mod, fun, arity) do
476+
:erlang.make_fun(mod, fun, arity)
477+
end
478+
466479
@doc """
467480
Attaches documentation to a given function. It expects
468481
the module the function belongs to, the line (a non negative

lib/elixir/lib/stream.ex

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ defmodule Stream do
293293
294294
## Examples
295295
296-
iex> Stream.repeatedly(function(:random.uniform/0)) |> Enum.take(3)
296+
iex> Stream.repeatedly(&:random.uniform/0) |> Enum.take(3)
297297
[0.4435846174457203, 0.7230402056221108, 0.94581636451987]
298298
299299
"""
@@ -307,7 +307,6 @@ defmodule Stream do
307307
do_repeatedly(generator_fun, fun.(generator_fun.(), acc), fun)
308308
end
309309

310-
311310
@doc """
312311
Lazily takes the next `n` items from the enumerable and stops
313312
enumeration.

lib/elixir/src/elixir_fn.erl

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
11
-module(elixir_fn).
22
-export([fn/3, capture/3]).
33
-import(elixir_scope, [umergec/2]).
4+
-import(elixir_errors, [syntax_error/3, syntax_error/4, compile_error/4]).
45
-include("elixir.hrl").
56

7+
fn(Meta, Clauses, S) ->
8+
Transformer = fun({ ArgsWithGuards, CMeta, Expr }, Acc) ->
9+
{ Args, Guards } = elixir_clauses:extract_splat_guards(ArgsWithGuards),
10+
elixir_clauses:assigns_block(?line(CMeta), fun elixir_translator:translate/2, Args, [Expr], Guards, umergec(S, Acc))
11+
end,
12+
13+
{ TClauses, NS } = lists:mapfoldl(Transformer, S, Clauses),
14+
Arities = [length(Args) || { clause, _Line, Args, _Guards, _Exprs } <- TClauses],
15+
16+
case length(lists:usort(Arities)) of
17+
1 ->
18+
{ { 'fun', ?line(Meta), { clauses, TClauses } }, umergec(S, NS) };
19+
_ ->
20+
syntax_error(Meta, S#elixir_scope.file,
21+
"cannot mix clauses with different arities in function definition")
22+
end.
23+
624
capture(Meta, { '/', _, [{ { '.', _, [M, F] }, _ , [] }, A] }, S) when is_atom(F), is_integer(A) ->
725
{ [MF, FF, AF], SF } = elixir_translator:translate_args([M, F, A], S),
826
{ { 'fun', ?line(Meta), { function, MF, FF, AF } }, SF };
@@ -19,24 +37,11 @@ capture(Meta, { '/', _, [{ F, _, C }, A] }, S) when is_atom(F), is_integer(A), i
1937
end,
2038

2139
case elixir_dispatch:import_function(WrappedMeta, F, A, S) of
22-
false -> elixir_errors:compile_error(WrappedMeta, S#elixir_scope.file,
23-
"expected ~ts/~B to be a function, but it is a macro", [F, A]);
40+
false -> compile_error(WrappedMeta, S#elixir_scope.file,
41+
"expected ~ts/~B to be a function, but it is a macro", [F, A]);
2442
Else -> Else
25-
end.
26-
27-
fn(Meta, Clauses, S) ->
28-
Transformer = fun({ ArgsWithGuards, CMeta, Expr }, Acc) ->
29-
{ Args, Guards } = elixir_clauses:extract_splat_guards(ArgsWithGuards),
30-
elixir_clauses:assigns_block(?line(CMeta), fun elixir_translator:translate/2, Args, [Expr], Guards, umergec(S, Acc))
31-
end,
43+
end;
3244

33-
{ TClauses, NS } = lists:mapfoldl(Transformer, S, Clauses),
34-
Arities = [length(Args) || { clause, _Line, Args, _Guards, _Exprs } <- TClauses],
35-
36-
case length(lists:usort(Arities)) of
37-
1 ->
38-
{ { 'fun', ?line(Meta), { clauses, TClauses } }, umergec(S, NS) };
39-
_ ->
40-
elixir_errors:syntax_error(Meta, S#elixir_scope.file,
41-
"cannot mix clauses with different arities in function definition")
42-
end.
45+
capture(Meta, Arg, S) ->
46+
syntax_error(Meta, S#elixir_scope.file,
47+
"invalid args for &: ~ts", ['Elixir.Macro':to_string(Arg)]).

lib/elixir/src/elixir_macros.erl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,22 @@ translate({ in, Meta, [Left, Right] }, #elixir_scope{extra_guards=Extra} = S) ->
4949
{ TVar, TS#elixir_scope{extra_guards=[TExpr|Extra]} };
5050

5151
%% Functions
52-
%% Once this function is removed, the related checks
53-
%% from quote needs to be removed too.
52+
%% Once this function is removed, the related checks from quote needs to be removed too.
53+
%% We also need to remove it from the Kernel in erlang list.
5454

5555
translate({ function, Meta, [[{do,{ '->',_,Pairs}}]] }, S) ->
56+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "function do ... end is deprecated, please use fn ... end instead"),
5657
assert_no_match_or_guard_scope(Meta, 'function', S),
5758
elixir_fn:fn(Meta, Pairs, S);
5859

5960
translate({ function, _, [{ '/', _, [{{ '.', Meta, [M, F] }, _ , []}, A]}] }, S) when is_atom(F), is_integer(A) ->
60-
translate({ function, Meta, [M, F, A] }, S);
61+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "function(Mod.fun/a) is deprecated, please use &Mod.fun/a instead"),
62+
assert_no_match_or_guard_scope(Meta, 'function', S),
63+
{ [A0,B0,C0], SA } = translate_args([M, F, A], S),
64+
{ { 'fun', ?line(Meta), { function, A0, B0, C0 } }, SA };
6165

6266
translate({ function, MetaFA, [{ '/', _, [{F, Meta, C}, A]}] }, S) when is_atom(F), is_integer(A), is_atom(C) ->
67+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "function(fun/a) is deprecated, please use &fun/a instead"),
6368
assert_no_match_or_guard_scope(Meta, 'function', S),
6469

6570
WrappedMeta =
@@ -84,6 +89,7 @@ translate({ function, Meta, [Arg] }, S) ->
8489
['Elixir.Macro':to_string(Arg)]);
8590

8691
translate({ function, Meta, [_,_,_] = Args }, S) when is_list(Args) ->
92+
elixir_errors:deprecation(Meta, S#elixir_scope.file, "function/3 is deprecated, please use Module.function/3 instead"),
8793
assert_no_match_or_guard_scope(Meta, 'function', S),
8894
{ [A,B,C], SA } = translate_args(Args, S),
8995
{ { 'fun', ?line(Meta), { function, A, B, C } }, SA };

lib/elixir/src/elixir_partials.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ convert([{'|', Meta, [_, _] = Args}|T], S, allow_tail, CallAcc, DefAcc) ->
4141
{ NewArgs, NewDef, NewS } = convert(Args, S, allow_tail, [], DefAcc),
4242
convert(T, NewS, allow_tail, [{ '|', Meta, NewArgs}|CallAcc], NewDef);
4343

44-
convert([{'&', Meta, [Pos]}|T], S, Opt, CallAcc, DefAcc) ->
44+
convert([{'&', Meta, [Pos]}|T], S, Opt, CallAcc, DefAcc) when is_integer(Pos) ->
4545
case lists:keyfind(Pos, 1, DefAcc) of
4646
false ->
4747
{ Var, SC } = elixir_scope:build_ex_var(?line(Meta), S),

lib/elixir/src/elixir_quote.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ do_quote({ { '.', Meta, [Left, unquote] }, _, [Expr] }, #elixir_quote{unquote=tr
152152

153153
do_quote({ '&', Meta, [{ '/', _, [{F, _, C}, A]}] = Args },
154154
#elixir_quote{imports_hygiene=true} = Q, S) when is_atom(F), is_integer(A), is_atom(C) ->
155-
do_quote_fa(function, Meta, Args, F, A, Q, S);
155+
do_quote_fa('&', Meta, Args, F, A, Q, S);
156156

157157
do_quote({ function, Meta, [{ '/', _, [{F, _, C}, A]}] = Args },
158158
#elixir_quote{imports_hygiene=true} = Q, S) when is_atom(F), is_integer(A), is_atom(C) ->

lib/elixir/test/elixir/inspect_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,12 @@ defmodule Inspect.OthersTest do
224224
end
225225

226226
test :external_elixir_funs do
227-
bin = inspect(function(Enum.map/2))
227+
bin = inspect(&Enum.map/2)
228228
assert bin == "&Enum.map/2"
229229
end
230230

231231
test :external_erlang_funs do
232-
bin = inspect(function(:lists.map/2))
232+
bin = inspect(&:lists.map/2)
233233
assert bin == "&:lists.map/2"
234234
end
235235

0 commit comments

Comments
 (0)