W0071 - Redundant Fun Wrapper
Information
use_fun(L) ->
lists:map(fun(X) -> foo(X) end, L).
%% ^^^^^^^^^^^^^^^^^^^^ 💡 information: W0071: A reference to `fun foo/1` can be taken directly.
Explanation
This diagnostic detects anonymous functions that are redundant wrappers around another function call with the same arguments in the same order. These wrappers can be replaced with direct fun references, making the code cleaner and more efficient.
Patterns Detected
The linter identifies three types of redundant wrappers:
Local Function Calls
% Before - redundant wrapper
lists:map(fun(X) -> foo(X) end, L).
lists:foldl(fun(X, Y) -> add(X, Y) end, 0, L).
% After - direct fun reference
lists:map(fun foo/1, L).
lists:foldl(fun add/2, 0, L).
Remote Function Calls
% Before - redundant wrapper
lists:map(fun(X) -> erlang:abs(X) end, L).
lists:foldl(fun(X, Y) -> erlang:max(X, Y) end, 0, L).
% After - direct fun reference
lists:map(fun erlang:abs/1, L).
lists:foldl(fun erlang:max/2, 0, L).
Fun Variables
% Before - redundant wrapper (F is already a fun)
apply_fun(F, L) ->
lists:map(fun(X) -> F(X) end, L).
% After - use F directly
apply_fun(F, L) ->
lists:map(F, L).
Why This Matters
- Readability: Direct fun references are more concise and clearly express intent
- Performance: Eliminates an unnecessary function call wrapper
- Maintainability: Less code means fewer places for bugs to hide
When NOT to Simplify
The linter correctly ignores cases where the anonymous function is NOT a simple wrapper:
- Arguments are reordered:
fun(X, Y) -> foo(Y, X) end - Extra arguments are captured:
fun(X) -> foo(X, Captured) end - Nested calls:
fun(X) -> foo(bar(X)) end - Body is not a call:
fun(X) -> X + 1 end - Pattern matching in parameters:
fun({X, Y}) -> foo(X, Y) end - Guards are present:
fun(X) when X > 0 -> foo(X) end - Multiple clauses:
fun(0) -> zero; (X) -> X end - Named funs:
fun Loop(X) -> foo(X) end
Auto-Fix
This diagnostic includes an auto-fix that replaces the redundant wrapper with the appropriate fun reference or variable.