summaryrefslogtreecommitdiff
path: root/system/doc/reference_manual/opaques.xml
diff options
context:
space:
mode:
Diffstat (limited to 'system/doc/reference_manual/opaques.xml')
-rw-r--r--system/doc/reference_manual/opaques.xml91
1 files changed, 48 insertions, 43 deletions
diff --git a/system/doc/reference_manual/opaques.xml b/system/doc/reference_manual/opaques.xml
index 8fa3bae3e5..ad71ed111c 100644
--- a/system/doc/reference_manual/opaques.xml
+++ b/system/doc/reference_manual/opaques.xml
@@ -33,28 +33,30 @@
<section>
<title>Opaque Type Aliases</title>
- <p>The main use case for opacity in Erlang is to hide the implementation of a data type, enabling evolving the API while minimizing the risk of breaking consumers. The runtime does not check opacity. Dialyzer provides some opacity-checking, but the rest is up to convention.
+ <p>The main use case for opacity in Erlang is to hide the
+ implementation of a data type, enabling evolving the API while
+ minimizing the risk of breaking consumers. The runtime does not
+ check opacity. Dialyzer provides some opacity-checking, but the
+ rest is up to convention.
</p>
<p>
- This document explains what Erlang opacity is (and the trade-offs involved) via the example of OTP's
- <c>sets:set()</c>
- data type. This type
- <em>was</em>
- defined in `sets` module like this:
+ This document explains what Erlang opacity is (and the
+ trade-offs involved) via the example of the <c>sets:set()</c>
+ data type. This type <em>was</em> defined in the <c>sets</c>
+ module like this:
</p>
<code type="erl">-opaque set(Element) :: #set{segs :: segs(Element)}.</code>
- <p>OTP 24 changed the definition to the following, in
- <url href="https://github.com/erlang/otp/commit/e66941e8d7c47b973dff94c0308ea85a6be1958e">this commit</url>
+ <p>OTP 24 changed the definition to the following in
+ <url href="https://github.com/erlang/otp/commit/e66941e8d7c47b973dff94c0308ea85a6be1958e">this commit</url>.
</p>
<code type="erl">-opaque set(Element) :: #set{segs :: segs(Element)} | #{Element => ?VALUE}.</code>
<p>
- And this change was safer and more backwards-compatible than if the type had been defined with
- <c>-type</c>
- instead of
- <c>-opaque</c>
- . Here's why: when a module defines an
- <c>-opaque</c>
- , the contract is that only the defining module should rely on the definition of the type: no other modules should rely on the definition.
+ And this change was safer and more backwards-compatible than if
+ the type had been defined with <c>-type</c> instead of
+ <c>-opaque</c>. Here is why: when a module defines an
+ <c>-opaque</c>, the contract is that only the defining module
+ should rely on the definition of the type: no other modules
+ should rely on the definition.
</p>
<p>
This means that code that pattern-matched on
@@ -62,8 +64,8 @@
as a record/tuple technically broke the contract, and opted in to being potentially broken when the definition of
<c>set()</c>
changed. Before OTP 24, this code printed
- <c>ok</c>
- . In OTP 24 it may error:
+ <c>ok</c>.
+ In OTP 24 it may error:
</p>
<code type="erl">
case sets:new() of
@@ -85,12 +87,9 @@ end.
Instead, use functions provided by the module for working with the type. For example,
<c>sets</c>
module provides
- <c>sets:new/0</c>
- ,
- <c>sets:add/2</c>
- ,
- <c>sets:is_element/2</c>
- , etc.
+ <c>sets:new/0</c>,
+ <c>sets:add/2</c>,
+ <c>sets:is_element/2</c>, and so on.
</item>
<item>
<c>sets:set(a)</c>
@@ -108,34 +107,40 @@ end.
</p>
<list type="bulleted">
<item>
- Since consumers are expected to not rely on the definition of the opaque type, you must provide functions for constructing and querying/deconstructing intances of your opaque type. For example, sets can be constructed with
- <c>sets:new/0</c>, <c>sets:from_list/1</c>, <c>sets:add/2</c>, queried with <c>sets:is_element/2</c>, and deconstructed with<c>sets:to_list/1</c>.
+ Since consumers are expected to not rely on the definition of
+ the opaque type, you must provide functions for constructing,
+ querying, and deconstructing instances of your opaque type. For
+ example, sets can be constructed with <c>sets:new/0</c>,
+ <c>sets:from_list/1</c>, <c>sets:add/2</c>, queried with
+ <c>sets:is_element/2</c>, and deconstructed
+ with<c>sets:to_list/1</c>.
</item>
<item>
- Don't define an opaque with a type variable in parameter position. This breaks the normal and expected behavior that (for example)
- <c>my_type(a)</c> is a subtype of <c>my_type(a | b)</c>
+ Don't define an opaque with a type variable in parameter
+ position. This breaks the normal and expected behavior that
+ (for example) <c>my_type(a)</c> is a subtype of <c>my_type(a |
+ b)</c>
</item>
<item>
Add <seeguide marker="typespec">specs</seeguide> to exported functions that use the opaque type
</item>
</list>
- <p>Note that opaques can be harder to work with for consumers, since the consumer is expected not to pattern-match and must instead use functions that the author of the opaque type provides to use instances of the type.</p>
+ <p>Note that opaques can be harder to work with for consumers,
+ since the consumer is expected not to pattern-match and must
+ instead use functions that the author of the opaque type provides
+ to use instances of the type.</p>
<p>
- Also, opacity in Erlang is skin-deep: the runtime does not enforce opacity-checking. So now that sets are implemented in terms of maps, an
- <c>is_map</c>
- check on a set
- <em>will</em>
- pass. The opacity rules are only enforced by convention and by additional tooling such as Dialyzer. And this enforcement is not total: For example, determined consumer of
- <c>sets</c>
- can still do things that reveal the structure of the set, such as by printing, serializing, or using a set as
- <c>term()</c>
- and then inspecting via functions like
- <c>is_map</c>
- or
- <c>maps:get/2</c>
- . And Dialyzer must make some
- <url href="https://github.com/erlang/otp/issues/5118">approximations</url>
- . Opacity checking has limitations, but is still a vital tool in scalable Erlang development.
+ Also, opacity in Erlang is skin-deep: the runtime does not
+ enforce opacity-checking. So now that sets are implemented in
+ terms of maps, an <c>is_map/1</c> check on a set <em>will</em>
+ pass. The opacity rules are only enforced by convention and by
+ additional tooling such as Dialyzer, and this enforcement is not
+ total. A determined consumer of <c>sets</c> can still reveal the
+ structure of the set, for example by printing, serializing, or
+ using a set as a <c>term()</c> and inspecting it via functions
+ like <c>is_map/1</c> or <c>maps:get/2</c>. Also, Dialyzer must make
+ some <url
+ href="https://github.com/erlang/otp/issues/5118">approximations</url>.
</p>
</section>
</chapter>