Erlang/OTP 29

This release of Erlang/OTP can be built from source or installed using pre-built packages for your OS or third-party tools (such as kerl or asdf).

docker run -it erlang:29

Check out the git tag OTP-29.0, and build a full OTP system including documentation.

HIGHLIGHTS

  • The JIT now generates better code for matching or creating binaries with multiple little-endian segments.

  • In the documentation for the [compile] module, a section has been added with recommendations for implementors of languages running on the BEAM. Documentation has also been added for the to_abstr, to_exp, and from_abstr options.

    The documentation for [erlc] now lists .abstr as one of the supported options.

    When compiling with the to_abstr option, the resulting .abstr file now retains any -doc attributes present in the source code.

  • Native records as described in [EEP-79] has been implemented.

    A native record is a data structure similar to the traditional tuple-based records, except that is a true data type.

    Native records are considered experimental in Erlang/OTP 29 and possibly also in Erlang/OTP 30, meaning that their behavior may change, potentially requiring updates to applications that use them.

  • The guard BIF is_integer/3 has been added. It follows the design of the original EEP-16, only changing the name from is_between to is_integer. This BIF takes in 3 parameters, Term, LowerBound, and UpperBound.

    It returns true if Term, LowerBound, and UpperBound are all integers, and LowerBound =< Term =< UpperBound; otherwise, it returns false.

    Example:

    1> I = 42.
    2> is_integer(I, 0, 100).
    true
    
  • There are new functions for random permutation of a list: rand:shuffle/1 and rand:shuffle_s/2. They are inspired by a suggestion and discussion on ErlangForums.

  • In the default code path for the Erlang system, the current working directory (.) is now in the last position instead of the first.

    *** POTENTIAL INCOMPATIBILITY ***

  • Function application is now left associative. That means one can now write:

    f(X)(Y)
    

    instead of:

    (f(X))(Y)
    
  • The old-style type tests in guards (integer, atom, and so on) have been scheduled for removal in Erlang/OTP 30. They have been deprecated for a long time.

  • There will now be a warning when exporting variables out of a subexpression. For example:

    case file:open(File, AllOpts = [write,{encoding,utf8}]) of
        {ok,Fd} ->
            {Fd,AllOpts}
    end
    

    To avoid the warning, this can be rewritten to:

    AllOpts = [write,{encoding,utf8}],
    case file:open(File, AllOpts) of
        {ok,Fd} ->
            {Fd,AllOpts}
    end
    

    The warning can be suppressed by giving option nowarn_export_var_subexpr to the compiler.

  • There is a new option warn_obsolete_bool_op that instruct the compiler to emit warnings for the and and or operators. It is recommended to instead use the modern andalso and orelse operators, or , and ; in guards.

  • graph is a new module that is a functional equivalent of the [digraph] and [digraph_utils] modules.

  • Before Erlang/OTP 29, attempting to bind variables in a comprehension would compile successfully but fail at runtime. Example:

    1> fh(List) -> [H || E <- List, H = erlang:phash2(E), H rem 10 =:= 0].
    ok
    2> fh(lists:seq(1, 10)).
    * exception error: bad filter 2614250
    

    In Erlang/OTP 29, attempting to bind a variable in a comprehension will fail by default:

    1> fh(List) -> [H || E <- List, H = erlang:phash2(E), H rem 10 =:= 0].
    * 5:14: matches using '=' are not allowed in comprehension qualifiers
    unless the experimental 'compr_assign' language feature is enabled.
    With 'compr_assign' enabled, a match 'P = E' will behave as a
    strict generator 'P <-:- [E]'."
    

    However, this example will work as expected if the compr_assign feature is enabled when starting the runtime system:

    $ erl -enable-feature compr_assign
    . . .
    1> fh(List) -> [H || E <- List, H = erlang:phash2(E), H rem 10 =:= 0].
    ok
    2> fh(lists:seq(1, 10)).
    [2614250]
    

    Here is another example how compr_assign can be used:

    -module(example).
    -feature(compr_assign, enable).
    -export([cat/1]).
    
    cat(Files) ->
        [Char || F <- Files,
                 {ok, Bin} = file:read_file(F),
                 Char <- unicode:characters_to_list(Bin)].
    

    *** POTENTIAL INCOMPATIBILITY ***

  • There will now be a warning when using the catch operator, which has been deprecated for a long time.

    It is recommended to instead use trycatchend but is also possible to disable the warning by using the nowarn_deprecated_catch option.

  • Multi-valued comprehensions according to [EEP 78] has been implemented.

    Example:

    > [I, -I || I <- lists:seq(1, 5)].
    [1,-1,2,-2,3,-3,4,-4,5,-5]
    
  • There will now be a warning for matches that unify constructors, such as the following:

    m({a,B} = {Y,Z}) -> . . .
    

    Such a match can be rewritten to:

    m({a=Y,B=Y}) -> . . .
    

    The compiler option nowarn_match_alias_pats can be used to disable the warning.

  • There is no longer a 32-bit Erlang/OTP build for Windows.

  • While the iteration order for maps is undefined, it is now guaranteed that all ways of iterating over maps provides the elements in the same order. That is, all of the following ways of iterating will produce the elements in the same order:

    • maps:keys/1
    • maps:values/1
    • maps:to_list/1
    • maps:to_list(maps:iterator(M))
    • Map comprehension: [{K,V} || K := V <- M]
  • The default key exchange algorithm is now mlkem768x25519-sha256, a hybrid quantum-resistant algorithm combining ML-KEM-768 with X25519. This provides protection against both classical and quantum computer attacks while maintaining backward compatibility through automatic fallback to other algorithms when peers don’t support it.

    *** POTENTIAL INCOMPATIBILITY ***

  • The compiler now generates more efficient code for map comprehensions with constant values that don’t depend on the generator, such as the following:

    #{K => 42} || K <- List}.
    #{K => X || K <- List}.
    #{K => {X, Y} || K <- List}.
    
  • The SSH daemon now defaults to disabled for shell and exec services, implementing the “secure by default” principle. This prevents authenticated users from executing arbitrary Erlang code unless explicitly configured.

    Applications requiring shell or exec functionality must now explicitly enable:

      %% Enable Erlang shell
      ssh:daemon(Port, [{shell, {shell, start, []}} | Options])
    
      %% Enable Erlang term evaluation via exec
      ssh:daemon(Port, [{exec, erlang_eval} | Options])
    
      %% Restore complete old behavior
      ssh:daemon(Port, [{shell, {shell, start, []}},
                        {exec, erlang_eval}
                        | Options])
    

    *** POTENTIAL INCOMPATIBILITY ***

  • The odbc application is now deprecated and is planned to be removed in Erlang/OTP 30.

    The [ftp] and [ct_ftp] modules are now deprecated and are planned to be removed in Erlang/OTP 30.

  • The array module have been extended with several new functions. The internal representation have been changed to allow the new functionality and optimizations. Arrays serialized with term_to_binary/1 in previous releases are not compatible.

    *** POTENTIAL INCOMPATIBILITY ***

  • Added support for socket functions recvmmsg() and sendmmsg().

  • m:erl_tar will use less memory when extracting large tar entries to disk. Instead of reading each tar entry into memory, [erl_tar] will now stream data in chunks of 64KB. The chunk size is settable using the new {chunks,ChunkSize} option.

    The new {max_size,Size} option will set a limit on the total size of extracted data to protect against filling up the disk.

    Checking of symlinks has been improved. Some symlinks that were safe (such as dir/link -> ../file) used to be rejected.

  • Added a new module called io_ansi that allows the user to emit Virtual Terminal Sequences (a.k.a. ANSI sequences) to the terminal in order to add colors/styling to text or create fully-fledged terminal applications.

    io_ansi uses the local terminfo database in order to be as cross-platform compatible as possible.

    It also works across nodes so that if functions on a remote node call io_ansi:fwrite/1 it will use the destination terminal’s terminfo database to determine which sequences to emit. In practice, this means that you can call functions in a remote shell session that use io_ansi and it will properly detect the terminal sequences the target terminal can handle and will print using them correctly.

  • The ignore_xref attribute has been handled as a post-analysis filter by build tools such as Rebar3. In this release, [xref] itself does the filtering, ensuring that all tooling that calls xref for any purpose can rely on these declarations to just work.

  • New in this release is ct_doctest, a module that allows the user to test documentation examples in Erlang module docs and documentation files.

    ct_doctest allows you to:

    • Test code examples using shell syntax and their returns
    • Test code examples that should fail
    • Write example modules that are compiled and available in shell examples
    • Plugin other documentation parsing engines so that examples in, for example, edoc, asciidoc, and others can also be tested.

    See the documentation for more details.

  • Added support for -unsafe attributes, which is used to mark functions as unsafe to use.

    This is similar to but separate from deprecation, and the compiler will by default now generate warnings for calls to functions in Erlang/OTP that are known to be always unsafe.

    Furthermore, [xref] can now be used to find calls to functions in another application that lack a -doc attribute (undocumented_function_calls), calls to functions in another application marked -doc false. (private_function_calls), as well as calls to unsafe functions (unsafe_function_calls).

    Own Id: OTP-20066
    Application(s): asn1, common_test, compiler, crypto, debugger, dialyzer, diameter, edoc, eunit, inets, kernel, megaco, mnesia, observer, odbc, os_mon, otp, parsetools, public_key, reltool, runtime_tools, sasl, ssh, ssl, stdlib, syntax_tools, tftp, tools, wx, xmerl
    Related Id(s): [PR-10839]

  • The post-quantum hybrid algorithm x25519mlkem768 is now the most preferred key exchange group in the default configuration.

    Post-quantum hybrid algorithms secp384r1mlkem1024 and secp256r1mlkem768 are supported but have to be configured. The same goes for the plain post-quantum algorithms mlkem1024, mlkem768, and mlkem512.

    The most preferred signature algorithms is now post-quantum algorithms ML-DSA followed by the fastest SLH-DSA (slh_dsa_sha2_256f) algorithm, if such a certificate is available in the configuration. Other SLH-DSA variants are also supported but are added to the end of the preferred list.

    All these algorithms were available in OTP-28.4 but none of them were preferred and some of them changed default status.

    *** POTENTIAL INCOMPATIBILITY ***

  • The [json] module now encodes and decodes quoted strings faster. Improvements of up to 55 percent has been measured when decoding JSON data with long strings.

    The string:length/1, string:slice/2, and string:slice/3 functions have been optimized. For some strings, they can be up to twice as fast.

  • The SFTP subsystem is no longer enabled by default when starting an SSH daemon. To enable it, add the subsystems option explicitly:

    ssh:daemon(Port, [{subsystems, [ssh_sftpd:subsystem_spec([])]} | Options])
    

    *** POTENTIAL INCOMPATIBILITY ***

  • The runtime system now supports generating encrypted crash dumps. See the description of --enable-encrypted-crash-dumps in [Building and Installing Erlang/OTP].

  • There is a new Hardening guide giving guidelines on how to strengthen the security for the ssl application.

  • There is a new Hardening guide with advice for configuring Inets to be more secure.