From b1cf16ae2ad65d41c7a507fd32c300fc9c650302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 9 Dec 2025 12:40:03 +0100 Subject: [PATCH 1/7] Add EEP on partially applied functions --- eeps/eep-00XX.md | 224 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 eeps/eep-00XX.md diff --git a/eeps/eep-00XX.md b/eeps/eep-00XX.md new file mode 100644 index 0000000..13af9c7 --- /dev/null +++ b/eeps/eep-00XX.md @@ -0,0 +1,224 @@ + Author: José Valim + Status: Active + Type: Process + Created: 09-Dec-2025 + Post-History: +**** +EEP XX: Partially applied functions +---- + +Abstract +======== + +This EEP proposes an alternative to "MFArgs" (Module-Function-Arguments): +three-element tuples where the first element is a module, the second is +a function name, and the third is a list of arguments. The proposed +alternative preserves the desired properties of MFArgs while being more +ergonomic and with none of MFArgs limitations. + +Rationale +========= + +Today, the use of MFArgs are pervasive in Erlang. Generally speaking, +an API accepts MFArgs either as tuples or as three distinct arguments. +MFArgs can be invoked as-is but quite often they have additional arguments +prepended, as shown below: + +```erlang +{Mod, Fun, Args} = MFArgs +apply(Mod, Fun, [SomeValue | Args]). +``` + +One of the main reasons MFArgs exist is because anonymous functions +which close over an existing environment cannot be serialized across +nodes nor be persisted to disk, so when dealing with distribution, +disk persistence, or hot code upgrades, you must carefully stick with +MFArgs. Similarly, configuration files do not support anonymous +functions, and MFArgs are the main option. + +Due to those limitations, many functions in Erlang/OTP and also in +libraries need to provide two APIs, one that accepts function types +and another for MFArgs. + +Despite their wide spread use, MFArgs come with several downsides: + +1. It is unclear which arity of the function will actually be invoked. + For example, `{some_mod, some_fun, [Arg1, Arg2]}` may have an + argument prepended when invoked, so what is invoked in practice + is `fun some_mod:some_fun/3`; + +2. Due to the above, they don't play well with `xref` or "go to + definition" used by editors; + +3. They are hard to evolve. Imagine you define an API that accepts + `{some_mod, some_fun, [Arg1, Arg2]}` and you prepend one argument. + In the future, users request for another agument to be prepended. + Using anonymous functions, you could use `is_function(Fun, Arity)` + to determine how many arguments are expected. With MFArgs, you can + use `erlang:function_exported/3`, but it may have false positives + (as in a higher arity function may exist for other purposes); + +4. They cause duplication in APIs, as APIs need to accept both `Fun` + and `MFArgs` as arguments; + +5. As we attempt to statically type programs, MFArgs cannot fully + statically check its arguments nor the return type. Consequently, + errors which could be caught statically, must now be handled + exclusively at runtime; + +Solution +======== + +Erlang must provide a contruct for partially applied functions. +Partially applied functions use the `fun Mod:Fun(...Args)` notation, +where arguments can also be placeholders given by the `_` variable. + +We will break down the syntax in the following section. For now, +let's see an example: + + 1> Fun = fun maps:get(username, _). + 2> Fun(#{username => "Joe"}). + "Joe" + +The above is equivalent to: + + 1> Fun = fun(X) -> maps:get(username, X) end. + 2> Fun(#{username => "Joe"}). + "Joe" + +While the proposed notation does provide syntactical affordances, +the most important aspect is that the function preserves its remote +name and arguments within the runtime. This means the partially +applied function can be passed across nodes or written to disk, +even if the module that defines the function is gone. + +Furthermore, partially applied functions can replace `MFArgs`, +removing all ambiguity about its behaviour. For example, imagine +the configuration below: + +```erlang +{some_config, {some_mod, some_fun, [answer, 42]}}. +``` + +If `some_config` is invoked with an additional argument, such +argument is not specified in the configuration definition itself, +therefore it is unclear which arity of `some_mod:some_fun` will +be invoked. But with partially applied functions, the number of +arguments is always clear, "go to definition" works, as config +files and static typing: + +```erlang +{some_config, fun some_mod:some_fun(_, answer, 42)}. +``` + +In practice, they solve all the downsides of `MFArgs` listed above: + +1. The arity is always clear; + +2. `xref` or "go to definition" can be unambiguously implemented; + +3. It is possible to handle different arities via + `is_function(Fun, Arity)` checks; + +4. There is no longer a need for MFArgs, functions are all you need; + +5. They can be statically checked; + +Syntax Specification +==================== + +The syntax of partially applied functions will be: + +```erlang +fun Fun(...Args) +fun Mod:Fun(...Args) +``` + +Where `Args` can be zero, one, or many arguments. Arguments +must be either literals or variables. The `_` variable denotes +placeholders, which are arguments that have not yet been +provided (the use of `_` is a proposal, the exact notation can +be changed). The number of placeholders dictate the arity of the +function and they are provided in order. For example: + +```erlang +fun hello(_, world, _) +``` + +is equivalent to: + +```erlang +fun(X, Y) -> hello(X, world, Y) end +``` + +Note Erlang will guarantee the applied arguments are either literals +or variables, ensuring the functions are indeed persistent across +nodes/modules. This is important because the role of this feature goes +beyond syntax sugar: it allows Erlang developers to glance at the code +and, as long as it uses `fun Mod:Fun/Arity` or `fun Mod:Fun(...Args)`, +they know they can be persisted. This information could also be used +by static analyzers and other features to lint code around distribution +properties. + +Note it should also be possible to apply all arguments, meaning a +zero-arity function is returned: + +```erlang +fun Mod:Fun(arg1, arg2, arg3) +``` + +Alternative Solutions +===================== + +The solution above chose to extend the existing `fun` syntax and use +`_` as a placeholder. Those exact details can be changed accordingly. + +Note this EEP focuses on language changes, rather than runtime changes, +because whatever solution is chosen must support configuration files, +which are limited in terms of code execution. This means an API that +worked exclusively at runtime would not tackle all of the use cases +handled by MFArgs. + +With that in mind, some alternatives were explored, which we mention +below. + +### `{Fun, Args}` Pairs + +One alternative is to support `{fun some_mod:some_fun/3, [Arg1, Arg2]}`. +This does improve a few things, as it makes the arity clear and "go to +definition" also works, but it still requires duplication across APIs, +as they need to support both regular functions and `{Fun, Args}` pairs. + +`{Fun, Args}` would likely allow us to type check the return type, but +the argument types could only be partially validated. + +### Additional Data Types + +Additional data types could also be introduced, for example, a +"serializable function" record which would be internally represented as +a MFArgs. Its major advantage is that it would not need changes to the +runtime and those working on type systems could type check these new +records accordingly. However, they would still force library developers +to define duplicate APIs that accept both serializable and regular +functions. + +Of course, we could change `is_function/2`, `apply/2`, and friends to +support this additional data type but, if we are ultimately changing +the Erlang runtime, I'd argue it is simpler and more productive to add +the serialization properties to functions, as done in this proposal, +than adding a new construct. + +Copyright +========= + +This document is placed in the public domain or under the CC0-1.0-Universal +license, whichever is more permissive. + +[EmacsVar]: <> "Local Variables:" +[EmacsVar]: <> "mode: indented-text" +[EmacsVar]: <> "indent-tabs-mode: nil" +[EmacsVar]: <> "sentence-end-double-space: t" +[EmacsVar]: <> "fill-column: 70" +[EmacsVar]: <> "coding: utf-8" +[EmacsVar]: <> "End:" +[VimVar]: <> " vim: set fileencoding=utf-8 expandtab shiftwidth=4 softtabstop=4: " From b4bd510779321fed59b7fb7805dc4168f615a6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 9 Dec 2025 12:44:17 +0100 Subject: [PATCH 2/7] Use --- for headers --- eeps/eep-00XX.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/eeps/eep-00XX.md b/eeps/eep-00XX.md index 13af9c7..4117aae 100644 --- a/eeps/eep-00XX.md +++ b/eeps/eep-00XX.md @@ -182,7 +182,8 @@ handled by MFArgs. With that in mind, some alternatives were explored, which we mention below. -### `{Fun, Args}` Pairs +`{Fun, Args}` Pairs +------------------- One alternative is to support `{fun some_mod:some_fun/3, [Arg1, Arg2]}`. This does improve a few things, as it makes the arity clear and "go to @@ -192,7 +193,8 @@ as they need to support both regular functions and `{Fun, Args}` pairs. `{Fun, Args}` would likely allow us to type check the return type, but the argument types could only be partially validated. -### Additional Data Types +Additional Data Types +--------------------- Additional data types could also be introduced, for example, a "serializable function" record which would be internally represented as From ddbb4150e7fd40b7cc908ae24d2ee037d036a0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 11 Dec 2025 14:31:27 +0100 Subject: [PATCH 3/7] Address feedback --- eeps/eep-00XX.md | 119 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 14 deletions(-) diff --git a/eeps/eep-00XX.md b/eeps/eep-00XX.md index 4117aae..3419d6d 100644 --- a/eeps/eep-00XX.md +++ b/eeps/eep-00XX.md @@ -61,10 +61,11 @@ Despite their wide spread use, MFArgs come with several downsides: 4. They cause duplication in APIs, as APIs need to accept both `Fun` and `MFArgs` as arguments; -5. As we attempt to statically type programs, MFArgs cannot fully - statically check its arguments nor the return type. Consequently, - errors which could be caught statically, must now be handled - exclusively at runtime; +5. As we attempt to statically type Erlang programs, MFArgs offer + limited opportunities for static verification, which either + becomes the source of dynamism (so errors that could be caught + statically must now be handled at runtime) or leads to false + positives (requiring developers to rewrite their code); Solution ======== @@ -135,11 +136,19 @@ fun Mod:Fun(...Args) ``` Where `Args` can be zero, one, or many arguments. Arguments -must be either literals or variables. The `_` variable denotes -placeholders, which are arguments that have not yet been -provided (the use of `_` is a proposal, the exact notation can -be changed). The number of placeholders dictate the arity of the -function and they are provided in order. For example: +must be either literals or bound variables. We have seen +literal examples above, but the arguments may also be variables: + +```erlang +Key = get_key(). +Fun = fun maps:get(Key, _). +``` + +The `_` variable denotes placeholders, which are arguments +that have not yet been provided (the use of `_` is a proposal, +the exact notation can be changed). The number of placeholders +dictate the arity of the function and they are provided in order. +For example: ```erlang fun hello(_, world, _) @@ -152,7 +161,7 @@ fun(X, Y) -> hello(X, world, Y) end ``` Note Erlang will guarantee the applied arguments are either literals -or variables, ensuring the functions are indeed persistent across +or bound variables, ensuring the functions are indeed persistent across nodes/modules. This is important because the role of this feature goes beyond syntax sugar: it allows Erlang developers to glance at the code and, as long as it uses `fun Mod:Fun/Arity` or `fun Mod:Fun(...Args)`, @@ -160,13 +169,63 @@ they know they can be persisted. This information could also be used by static analyzers and other features to lint code around distribution properties. -Note it should also be possible to apply all arguments, meaning a -zero-arity function is returned: +It should also be possible to partially apply a module/function pair +given by bound variables: + +```erlang +fun Mod:Fun(username, _) +``` + +However, it is not possible to partially apply an unknown local function +(mirror the fact that `fun Fun/0` is not valid today): + +```erlang +fun Fun(username, _) % will fail to compile +``` + +It is also possible to apply all arguments, meaning a zero-arity function +is returned: ```erlang fun Mod:Fun(arg1, arg2, arg3) ``` +Visual cluttering +----------------- + +Given Erlang also supports named functions, the differences +between named functions, partially applied, and regular +`Function/Arity` may be too small: + +```erlang +foo(Y) -> Y-1. +bar(X) -> + F1 = fun Foo(X) -> X+1 end, % Arity 1 + F2 = fun foo(X), % Arity 0 + F3 = fun foo/1, % Arity 1 + {F1(X), F2(), F3(X)}. +``` + +In case this is deemed a restriction, different options could be +considered: + +* Require all partially applied functions to have at least one `_`, + forbidding `fun foo(X)` or `fun some_mod:some_fun(Args)`. This does + add a syntactical annoyance but it does not remove any capability + as any function without placeholder can be written as a zero-arity + function; + +* Only allow remote partially applied functions, so `fun foo(_, ok)` + is invalid, but `fun some_mod:foo(_, ok)` is accepted. Unfortunately, + this may lead to developers doing external calls when a local call + would suffice; + +* Require partially applied functions to explicit list the arity too, + hence `fun foo(X)` has to be written as: `fun foo(X)/0`. + `fun maps:get(username, _)` as `fun maps:get(username, _)/1`. + If the version with arity is preferred, then the `fun` prefix could + also be dropped, if desired, as there is no ambiguity; + Alternative Solutions ===================== @@ -179,8 +238,7 @@ which are limited in terms of code execution. This means an API that worked exclusively at runtime would not tackle all of the use cases handled by MFArgs. -With that in mind, some alternatives were explored, which we mention -below. +With that in mind, we discuss some alternatives below. `{Fun, Args}` Pairs ------------------- @@ -210,6 +268,39 @@ the Erlang runtime, I'd argue it is simpler and more productive to add the serialization properties to functions, as done in this proposal, than adding a new construct. +Cuts from erlando +----------------- + +The [`erlando`](https://github.com/rabbitmq/erlando) project offered +the ability to partially apply functions (and also data structures). + +In particular, `erlando` does not require the `fun` prefix, so one can +write: + +```erlang +maps:get(username, _) +``` + +The lack of a prefix makes it harder to spot when a function is created +and also leads to visual ambiguity, such as in the code below: + +```erlang +list_to_binary([1, 2, math:pow(2, _)]) +``` + +Their documentation clarifies that it is always shallow (hence it applies +to `math:pow/2`). + +`erlando` also allows expressions of arbitrary complexity as argument: + +```erlang +maps:get(get_key_from(lists:flatten(Arg)), _) +``` + +This is intentionally disallowed in this proposal because one of the primary +goals of this proposal is to offer a clear syntactical affordance for functions +that are persistent across nodes. + Copyright ========= From 87a044a68e2f86fb21af05764c69af54bca985d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 11 Dec 2025 14:32:22 +0100 Subject: [PATCH 4/7] Lint --- eeps/eep-00XX.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eeps/eep-00XX.md b/eeps/eep-00XX.md index 3419d6d..86a8f1f 100644 --- a/eeps/eep-00XX.md +++ b/eeps/eep-00XX.md @@ -210,7 +210,7 @@ In case this is deemed a restriction, different options could be considered: * Require all partially applied functions to have at least one `_`, - forbidding `fun foo(X)` or `fun some_mod:some_fun(Args)`. This does + forbidding `fun foo(X)` or `fun some_mod:some_fun(Args)`. This does add a syntactical annoyance but it does not remove any capability as any function without placeholder can be written as a zero-arity function; @@ -284,7 +284,7 @@ maps:get(username, _) The lack of a prefix makes it harder to spot when a function is created and also leads to visual ambiguity, such as in the code below: -```erlang +```erlang list_to_binary([1, 2, math:pow(2, _)]) ``` From 63c6de4d231c54c8b41e2b171f0045791356670b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 16 Dec 2025 18:46:44 +0100 Subject: [PATCH 5/7] Apply last round of feedback --- eeps/eep-00XX.md | 101 +++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/eeps/eep-00XX.md b/eeps/eep-00XX.md index 86a8f1f..fa9a4d1 100644 --- a/eeps/eep-00XX.md +++ b/eeps/eep-00XX.md @@ -30,11 +30,11 @@ apply(Mod, Fun, [SomeValue | Args]). ``` One of the main reasons MFArgs exist is because anonymous functions -which close over an existing environment cannot be serialized across -nodes nor be persisted to disk, so when dealing with distribution, -disk persistence, or hot code upgrades, you must carefully stick with -MFArgs. Similarly, configuration files do not support anonymous -functions, and MFArgs are the main option. +which close over an existing environment can only be serialized across +nodes nor be persisted to disk if they preserve the same module version. +Therefore, when dealing with distribution, disk persistence, or hot code +upgrades, it is preferrable to use MFArgs instead. Similarly, configuration +files do not support anonymous functions, and MFArgs are the main option. Due to those limitations, many functions in Erlang/OTP and also in libraries need to provide two APIs, one that accepts function types @@ -81,17 +81,11 @@ let's see an example: 2> Fun(#{username => "Joe"}). "Joe" -The above is equivalent to: - - 1> Fun = fun(X) -> maps:get(username, X) end. - 2> Fun(#{username => "Joe"}). - "Joe" - While the proposed notation does provide syntactical affordances, the most important aspect is that the function preserves its remote name and arguments within the runtime. This means the partially applied function can be passed across nodes or written to disk, -even if the module that defines the function is gone. +even if the module that defines the function changes versions. Furthermore, partially applied functions can replace `MFArgs`, removing all ambiguity about its behaviour. For example, imagine @@ -131,64 +125,75 @@ Syntax Specification The syntax of partially applied functions will be: ```erlang -fun Fun(...Args) -fun Mod:Fun(...Args) +fun some_fun(...Args) +fun some_mod:some_fun(...Args) ``` -Where `Args` can be zero, one, or many arguments. Arguments -must be either literals or bound variables. We have seen -literal examples above, but the arguments may also be variables: +Where `Args` can be zero, one, or many arguments. Arguments can +be any expression or the `_` variable. The `_` variable denotes +placeholders, which are arguments that have not yet been provided +(the use of `_` is a proposal, the exact notation can be changed). +A partially applied function may have zero or more placeholders. +The number of placeholders dictate the arity of the function and +they are provided in order. For example, this is a two arity +function: ```erlang -Key = get_key(). -Fun = fun maps:get(Key, _). +%% two arity function +fun hello(_, world, _) ``` -The `_` variable denotes placeholders, which are arguments -that have not yet been provided (the use of `_` is a proposal, -the exact notation can be changed). The number of placeholders -dictate the arity of the function and they are provided in order. -For example: +The placeholder must always appear in the position of an argument, +it cannot be nested inside construct. The following is not allowed: ```erlang -fun hello(_, world, _) +fun hello({_, world}, _) +``` + +Furthermore, all arguments that are not placeholders are evaluted +**before** the function. Therefore, the following call: + +```erlang +spawn(fun ?MODULE:server_loop(self(), #{})) ``` is equivalent to: ```erlang -fun(X, Y) -> hello(X, world, Y) end +Arg1 = self(), +Arg2 = #{}, +spawn(fun() -> ?MODULE:server_loop(Arg1, Arg2) end). ``` -Note Erlang will guarantee the applied arguments are either literals -or bound variables, ensuring the functions are indeed persistent across -nodes/modules. This is important because the role of this feature goes -beyond syntax sugar: it allows Erlang developers to glance at the code -and, as long as it uses `fun Mod:Fun/Arity` or `fun Mod:Fun(...Args)`, -they know they can be persisted. This information could also be used -by static analyzers and other features to lint code around distribution +This is important because the role of this feature goes beyond syntax +sugar: it allows Erlang developers to glance at the code and, as long +as it uses `fun some_mod:some_fun/Arity` or `fun some_mod:some_fun(...Args)`, +they know they can be persisted. This information could also be used by +static analyzers and other features to lint code around distribution properties. -It should also be possible to partially apply a module/function pair +Bound variables +--------------- + +It is also possible to partially apply a module/function pair given by bound variables: ```erlang fun Mod:Fun(username, _) ``` -However, it is not possible to partially apply an unknown local function -(mirror the fact that `fun Fun/0` is not valid today): +It is also possible to partially apply another function: ```erlang -fun Fun(username, _) % will fail to compile +GetUsername = fun maps:get(username, _), +fun GetUsername(SomeMap). ``` -It is also possible to apply all arguments, meaning a zero-arity function -is returned: +The code above returns a zero-arity function that returns the +`username` of `SomeMap` when applied. -```erlang -fun Mod:Fun(arg1, arg2, arg3) -``` +However, note that `fun SomeFun/0` is not valid today, and +that syntax should still raise. Visual cluttering ----------------- @@ -291,15 +296,9 @@ list_to_binary([1, 2, math:pow(2, _)]) Their documentation clarifies that it is always shallow (hence it applies to `math:pow/2`). -`erlando` also allows expressions of arbitrary complexity as argument: - -```erlang -maps:get(get_key_from(lists:flatten(Arg)), _) -``` - -This is intentionally disallowed in this proposal because one of the primary -goals of this proposal is to offer a clear syntactical affordance for functions -that are persistent across nodes. +This proposal also specifies that all arguments must be evaluated before +the function is captured, so the functions can be persisted across nodes. +`erlando` does not implement such behaviour. Copyright ========= From da43090ca8a9c2c344a27f5f6fa6ae7322cb1772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 16 Dec 2025 18:50:24 +0100 Subject: [PATCH 6/7] Refactor Erlang code example and update terminology --- eeps/eep-00XX.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/eeps/eep-00XX.md b/eeps/eep-00XX.md index fa9a4d1..7608a3d 100644 --- a/eeps/eep-00XX.md +++ b/eeps/eep-00XX.md @@ -160,9 +160,11 @@ spawn(fun ?MODULE:server_loop(self(), #{})) is equivalent to: ```erlang -Arg1 = self(), -Arg2 = #{}, -spawn(fun() -> ?MODULE:server_loop(Arg1, Arg2) end). +spawn(begin + Arg1 = self(), + Arg2 = #{}, + fun() -> ?MODULE:server_loop(Arg1, Arg2) end +end). ``` This is important because the role of this feature goes beyond syntax @@ -172,8 +174,8 @@ they know they can be persisted. This information could also be used by static analyzers and other features to lint code around distribution properties. -Bound variables ---------------- +Bound variables as functions +---------------------------- It is also possible to partially apply a module/function pair given by bound variables: From 3afd0913fa10051a953ae82e6b77a1aa26df2c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 6 Jan 2026 16:23:10 +0100 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: Maria Scott <67057258+Maria-12648430@users.noreply.github.com> --- eeps/eep-00XX.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/eeps/eep-00XX.md b/eeps/eep-00XX.md index 7608a3d..8291ea1 100644 --- a/eeps/eep-00XX.md +++ b/eeps/eep-00XX.md @@ -33,7 +33,7 @@ One of the main reasons MFArgs exist is because anonymous functions which close over an existing environment can only be serialized across nodes nor be persisted to disk if they preserve the same module version. Therefore, when dealing with distribution, disk persistence, or hot code -upgrades, it is preferrable to use MFArgs instead. Similarly, configuration +upgrades, it is essential to use MFArgs instead. Similarly, configuration files do not support anonymous functions, and MFArgs are the main option. Due to those limitations, many functions in Erlang/OTP and also in @@ -70,7 +70,7 @@ Despite their wide spread use, MFArgs come with several downsides: Solution ======== -Erlang must provide a contruct for partially applied functions. +Erlang should provide a construct for partially applied functions. Partially applied functions use the `fun Mod:Fun(...Args)` notation, where arguments can also be placeholders given by the `_` variable. @@ -95,11 +95,11 @@ the configuration below: {some_config, {some_mod, some_fun, [answer, 42]}}. ``` -If `some_config` is invoked with an additional argument, such -argument is not specified in the configuration definition itself, +If `some_config` is invoked with additional arguments, those +arguments are not specified in the configuration definition itself, therefore it is unclear which arity of `some_mod:some_fun` will be invoked. But with partially applied functions, the number of -arguments is always clear, "go to definition" works, as config +arguments is always clear, "go to definition" works, as do config files and static typing: ```erlang @@ -144,13 +144,13 @@ fun hello(_, world, _) ``` The placeholder must always appear in the position of an argument, -it cannot be nested inside construct. The following is not allowed: +it cannot be nested inside a construct. The following is not allowed: ```erlang fun hello({_, world}, _) ``` -Furthermore, all arguments that are not placeholders are evaluted +Furthermore, all arguments that are not placeholders are evaluated **before** the function. Therefore, the following call: ```erlang @@ -167,7 +167,7 @@ spawn(begin end). ``` -This is important because the role of this feature goes beyond syntax +This is important because the role of this feature goes beyond syntactic sugar: it allows Erlang developers to glance at the code and, as long as it uses `fun some_mod:some_fun/Arity` or `fun some_mod:some_fun(...Args)`, they know they can be persisted. This information could also be used by @@ -195,13 +195,13 @@ The code above returns a zero-arity function that returns the `username` of `SomeMap` when applied. However, note that `fun SomeFun/0` is not valid today, and -that syntax should still raise. +such syntax will remain invalid. Visual cluttering ----------------- Given Erlang also supports named functions, the differences -between named functions, partially applied, and regular +between named functions, partially applied functions, and regular `Function/Arity` may be too small: ```erlang @@ -227,7 +227,7 @@ considered: this may lead to developers doing external calls when a local call would suffice; -* Require partially applied functions to explicit list the arity too, +* Require partially applied functions to explicitly list the arity too, hence `fun foo(X)` has to be written as: `fun foo(X)/0`. `fun maps:get(username, _)` as `fun maps:get(username, _)/1`. If the version with arity is preferred, then the `fun` prefix could @@ -237,7 +237,7 @@ Alternative Solutions ===================== The solution above chose to extend the existing `fun` syntax and use -`_` as a placeholder. Those exact details can be changed accordingly. +`_` as a placeholder. The exact details can be changed accordingly. Note this EEP focuses on language changes, rather than runtime changes, because whatever solution is chosen must support configuration files,