This repository exists to provide a small dependency that can be included in
Rebar-based Erlang/OTP projects to access
features in a version-independent manner. Obviously, anything you can do in
rebar
, you can do without it, but my goal is ease-of-use in rebar
.
If you're building without rebar, you can find an example of one way to
handle getting the OTP release in this project's Makefile
.
The general philosophy is to provide a common API across versions, starting
with (but not limited to) the global types that have moved into namespaces
as of OTP-17. For example, the dict
type has moved to the dict
namespace
and is deprecated in the global namespace. Using this package, you can use
that type as dict_t
regardless of the OTP version you're building with
(see below).
In addition to type mapping, there are other parts of the Erlang/OTP API that
have changed in recent versions, and I'm adding them as they cause me pain.
One of those, the move in the crypto API from distinct per-algoritm digest
functions to a set of common hash
functions, is partially addressed in the
crypto_hash.hrl
file, which defines macros that expand to the appropriate
functions at compile time (another reason I may move a bunch of this into a
parse_tranform
as mentioned below).
This work is published under an Apache license. The owner of the copyright may change, the license terms will not.
I've taken the approach of defining a macro to turn off current features
instead of using one (like the somewhat-common namespaced_types
approach)
to turn them on, on the assumption that once the target code evolves
to the point that it no longer supports OTP releases without the features, the
macro no longer needs to be defined at all.
Include the following entries in your rebar.config
file to turn on support
for older versions of OTP:
{erl_opts, [
. . .
% Include to use the xxx_t type definitions:
{platform_define, "^R[1-9]", no_otp_namespaced_types}
% Include to use the ?crypto_hash_xxx macros:
{platform_define, "^R1[0-5]", no_otp_crypto_hash}
. . .
]}.
{deps, [
. . .
{otp_compat, ".*", {git, "git://github.com/tburghart/otp_compat.git", {branch, "master"}}}
. . .
]}.
Do not use any branch other than master
unless you want much pain
and suffering ... you have been warned!
Then, in your Erlang source, include the following line to make the target
types accessible as typename_t
(see below).
-include_lib("otp_compat/include/ns_types.hrl").
The _t
suffix was chosen not because I want to make your Erlang code
look like C, but because it seemed like a pattern that would be a) easy to
remember and b) unlikely to conflict with existing Erlang code, which
generally eschews C-like conventions.
To use the crypto_hash_xxx
macros, you'd put the following in your source
-include_lib("otp_compat/include/crypto_hash.hrl").
then use (for example) ?crypto_hash_sha(...)
where you had previously used
crypto:sha(...)
or crypto:hash(sha, ...)
to get the correct function for
the version you're compiling with.
The macro ?NAMESPACED_TYPES_LIST
is defined as a list of the types declared
in the ns_types.hrl
file in a form suitable for use in -export_type()
.
As it seems likely that the list will be pretty static, they are reproduced
here:
array_t/0
array_t/1
dict_t/0
dict_t/2
digraph_t/0
gb_set_t/0
gb_set_t/1
gb_tree_t/0
gb_tree_t/2
queue_t/0
queue_t/1
set_t/0
set_t/1
tid_t/0
Along with type definitions, a short list of (possibly helpful) functions
are exposed. The public API documentation is generated by running make docs
.
Generally, the most interesting of these functions to users not rummaging
around in the weeds will be otp_compat:otp_version()
, which returns the
current OTP major release as an integer, saving you the annoyance of parsing
the variety of formats returned by erlang:system_info(otp_release)
.
Dialyzer prior to R16 will fail due to what it perceives to be duplicate type definitions for parameterized types. The Erlang compiler has no such problem, and since the target audience for this package is developers using newer, not older, versions of Erlang, I've chosen not to bother adding more macros to get around it.
If you come across issues other than the above, please do file them, and I'll have a look.
Ideally, I'll be able to work out the details of making the types available dynamically at runtime based on the running OTP release before OTP-18 (which removes them from the global namespace entirely) sees wide adoption, but that code's not ready for prime time quite yet. In the interim, code compiled with OTP-R16 or earlier may not work properly on OTP-18, and code compiled with OTP-17 or later may not work properly on OTP-R16 or earlier.
Unless you're rooting about in beam file internals, you're unlikely to stumble
across any problems with the current static compile-time typing, even across
OTP versions. Dialyzer, however, does just that, so if you compile a beam on
one side of the type change boundary and run dialyzer from the other side of
the boundary on it, expect copious warnings (see Issues about
outright dialyzer breakage in older versions).
Realistically, dialyzer is generally a build step, so it should be in sync,
but if you're running it against sources with the --src
option be sure
no_otp_namespaced_types
is defined (or not) as it is for compilation.
I'm also leaning toward making the whole thing a parse_tranform
, which will
allow me to add new capabilities without changing target source code at all.
Among those capabilities would be options selecting whether the result of
the transformation should be targeted at only the OTP version on which the
compilation is performed (no performance impact on mapped functions), or on
any OTP version (some performance impact using dynamically-mapped functions).
I believe that a lot of the potential performance impact can be alleviated
using some of Erlang's nifty dynamic code loading capabilities, but it'll
probably be a while before I have the time to dig into that.
The possibilities are not technically endless, but there's far more that can be done transforming the intermediate forms than I can do with static macros and type definitions alone.
This is very much a work in progress, More to Come ...