Introduce a way to use new BEAM features sooner


I’d like to propose we introduce a way to use newer features of Erlang/OTP sooner. 1.5 years is a long time to wait for new functionality to be available, especially if you are already using the most recent Erlang/OTP. These proposals are only about adding new functions and macros that depend on things available in newer versions of Erlang/OTP.

Proposal 1

This is the completely manual approach.

Conditionally compile a function or macro that uses a new function or raises an exception depending on the OTP version. For example:

defmodule Kernel do
  @otp_version ["21", "0"]
  @doc """
  Only supported on OTP versions equal to or greater than #{@otp_version}
  if otp_version() >= @otp_version do
    defmacro is_struct(struct), do: #implementation
    defmacro is_struct(struct), do: raise NotSupportedError, "is_struct/1 requires Erlang/OTP #{@otp_version} or greater"

Proposal 2

The automatic version of above.

defmodule Kernel do
  @doc """
  Normal docs
  @otp_version ["21", "0"]
  defmacro is_struct(struct), do: #implementation

In this version Elixir would see the @otp_version module attribute and only introduce the function/macro if the version met the requirements. Otherwise it would generate a function/macro that raised the appropriate exception. ExDoc would read @otp_version and include relevant information in docs. This would be similar to how @deprecated works today.

Discussion: Incorporating Erlang/OTP 21 map guards in Elixir

Especially since erlang has a relatively fixed schedule of one release per year.

So 6 months after a new feature set is available, we can benefit from the featureset available before that.

And to be honest, I wouldn’t care if 1.7/20 would miss features I had with 1.7/21. I usually am aware of my system and control it to my needs.

But this distinction between runtime and language is not as clear to many others as it is for me. Some people might wonder why that map specific guard works on their dev laptop but not on the host, while both have Elixir 1.7.0 installed.

PS: All versions mentioned in this post are choosen more or less randomly, mainly because thats the most recent/most upcoming releases in the overal discussion.


@blatyo I think it’s awesome idea!

How about just add erlang key support (for example: erlang: "~> 21.0) in project function in mix.exs? In such way we can require Erlang version for our project, so whole project will not compile on lower Erlang versions. New Elixir release will then change default Erlang version (for example from ~> 20.0 to ~> 21.0) for newly created projects.


This is unfortunately not enough, you need to use an Elixir version compiled against the particular OTP version you are using also. For this to work you would need a version manager that parses the mixfile and use the appropriately compiled elixir version for the given :elixir and :erlang requirements.


This should not be a problem, since Elixir compiled with lower Erlang version does not fail when FunctionClauseError is printed into console, so we can definitely catch such case, right?

1 Like

There are more incompatibilities than FunctionClauseError, for example the proposals in the first post in this thread suggests compiling Elixir against OTP 21 and older should produce different code for is_struct/1. This means you need to have an Elixir compiled with OTP 21 for is_struct to work.


Do people have a preference between Proposal 1 and 2?

The second would be something you could use in your own libraries/projects, similar to how @deprecated is used today.


Definitely 2nd proposal. We should avoid extra indention if it’s really not required.


I’ve been thinking about this and it seems like it would be simpler to just implement these as a package. The package could then do what I described here. It would mean you don’t need elixir to be compiled with a specific version of OTP, just the library. So, you could use OTP 21 to run Elixir 1.7 (compiled with OTP 18). That seems a lot simpler to me. There are two main drawbacks, 1) you need to install an external library and 2) you need to import the macros and functions, since they wouldn’t be defined on Kernel.