Note: this is a language proposal so please keep the discussion on topic. If you want to talk about related behaviour but not strictly part of the proposal, please start a new conversation.
Elixir focuses on good warning and error messages whenever possible. After all, an unclear warning/error should be a bug.
In some cases, however, to keep those messages as clear as possible, they end-up spanning multiple lines:
iex(1)> defmodule Foo do
...(1)> def bar(baz) do
...(1)> if true do
...(1)> baz = :other
...(1)> end
...(1)> end
...(1)> end
warning: variable "baz" is unused
Note variables defined inside case, cond, fn, if and similar do not leak. If you want to conditionally override an existing variable "baz", you will have to explicitly return the variable. For example:
if some_condition? do
atom = :one
else
atom = :two
end
should be written as
atom =
if some_condition? do
:one
else
:two
end
Unused variable "baz" found at:
iex:4
here is another example:
iex(2)> defmodule Bar do
...(2)> def foo(:a, b \\ :omg), do: :a
...(2)> def foo(:b, b), do: b
...(2)> end
warning: def foo/2 has multiple clauses and also declares default values. In such cases, the default values should be defined in a header. Instead of:
def foo(:first_clause, b \\ :default) do ... end
def foo(:second_clause, b) do ... end
one should write:
def foo(a, b \\ :default)
def foo(:first_clause, b) do ... end
def foo(:second_clause, b) do ... end
iex:4
The downside of those messages are that, for experienced developers, they end-up being too much noise. There is also a “scare” factor when you download a dependency and it ends-up printing long multiple lines of warnings.
This is aggravated by the fact that Elixir does not allow warnings to be disabled. We promise to keep all warnings relevant and worth of your time, but, as a trade-off, we don’t allow you to disable them.
Note: this is NOT a discussion about disabling or removing some warnings. If there are warnings you don’t agree with, please open up a separate discussion.
In order to keep our promise of relevant warnings for new and experienced developers alike, we would like to introduce help catalogs. This proposal is broken in 3 parts. First we introduce the idea of a help catalog. Then we propose a particular implementation. Finally we discuss improvements in related areas.
Help catalogs
The idea behind help catalogs is very simple. Instead of long warnings, we will provide users with a mechanism to get more information about that warning. For example, the unused variable warning above could be written as:
warning: nested variable "baz" is unused (elixir --explain nested_var)
Once the user invokes the proposed command, they will get a detailed explanation about the warning and how to fix it:
$ elixir --explain nested_var
This warning yada yada yada yada yada yada
yada yada yada.
For the clauses one, we could say:
warning: def foo/2 has multiple clauses and also declares default values. Please define default values in a header (elixir --explain defaults_and_clauses)
Do not worry about the styling of the warnings and of the command for now. We will address them later.
I believe this will be an improvement for long warnings but there is another reason why I believe this feature can be extremely useful. Let’s get a very simple warning, the unused variable warning, as seen below:
iex(3)> defmodule Baz do
...(3)> def bar(used, unused), do: used
...(3)> end
warning: variable "unused" is unused
iex:5
How to address this warning? We change unused
to _unused
. But how would somebody in their first week with Elixir know this is the case? We could make the warning longer but everyone would agree it is counter-productive. By having a catalog, we can include detailed information even on simple warnings like above. Similar feature exists in languages like Rust and PureScript.
Question 1: what do think about the idea of supporting help catalog in general? (regardless of the command structure, syntax, etc)
Implementing catalogs
If agreed that help catalogs will be a good addition to the language, then it is time to talk about its implementation.
In the examples above, we have used the following syntax to invoke them: elixir --explain nested_var
. Such syntax has two issues:
-
It seems specific to Elixir. However, as an extensible language, it would be great if help catalogs were available to all libraries
-
If we also want to introduce the
explain
functionality toIEx
, a natural confusion betweenexplain
andhelp
would arise. After all, what is the difference betweenhelp
andexplain
? When to use one or the other?
Therefore, I propose the following syntax for help catalogs:
$ elixir -h elixir:nested_var
$ elixir --help elixir:nested_var
Then in IEx, you can read a catalog as:
iex> h "elixir:nested_var"
In other words, we are simply extending the help mechanism to support catalogs. The catalog is given in two parts, the application name (elixir
) and the entry name (nested_var
), separated by :
.
Implementation wise, it will work like this:
- We will receive the entry name as “app:entry” and split it by “:”
- We get the application name and look for its
.app
file - Inside the app file, we will look for an entry named
help_catalog
that points to a module - The
help_catalog
will then be loaded and it must export a function of zero arity with the same name as the entry. The function should return a string in markdown that will then be formatted and printed.
Question 2: what do you think about the suggested syntax for catalogs and its implementation?
Closing the gap
Now that we have introduced elixir -h "app:entry"
, does it make sense to close the gap between the command line and IEx? In other words, should we be allowed to run elixir -h String
and show the documentation for the String
module?
One reason to say yes is completeness. However, I personally open IEx multiple times only to retrieve the documentation or to open a module, so I would definitely use this feature too.
In particular, I propose to support all of --help
, --type-help
, --behaviour-help
and --open
in the elixir
command line. I think being able to do elixir --open String
and have the module open in my editor would be fantastic.
Implementation-wise, this is very straight-forward, as all of those features already exist in IEx, and we are simply discussing an option to make it available in more places.
Note that we will also have to support those entries in mix run
, as we also need them available in the context of a project.
Question 3: should we close the gap and allow help and open generally available in the elixir
and mix run
commands?
Feedback
Feedback on the proposal is welcome. If you agree or disagree with the proposal, make sure to detail why, and remember to provide insight on the questions above. Thanks!