Skip to content

Commit

Permalink
Assorted Record Rules
Browse files Browse the repository at this point in the history
  • Loading branch information
elbrujohalcon committed Dec 16, 2014
1 parent f781329 commit 1fdd1ac
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
PROJECT = erlang_standards

include erlang.mk
include erlang.mk
42 changes: 36 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,13 @@ Table of Contents:
* [No Macros](#no-macros)
* [Uppercase Macros](#uppercase-macros)
* [No module or function name macros](#no-module-or-function-name-macros)
+ [Records](#records)
* [Record names](#record-names)
* [Records go first](#records-go-first)
* [Don't share your records](#dont-share-your-records)
* [Avoid records in specs](#avoid-records-in-specs)
* [Misc](#misc)
* [Write function specs](#write-function-specs)
* [Avoid records in specs](#avoid-records-in-specs)
* [Use -callback attributes over behaviour_info/1](use--callback-attributes-over-behaviour_info1)
* [No nested header inclusion](#no-nested-header-inclusion)
* [No types in include files](#no-types-in-include-files)
Expand Down Expand Up @@ -295,15 +299,31 @@ Erlang syntax is horrible amirite? So you might as well make the best of it, rig

*Reasoning*: Copying lines of code to the console for debugging (something that happens *a lot*) becomes a really hard task if we need to manually replace all the macros.

### Misc
### Records

***
##### Write function specs
> Write the **-spec**'s for your exported fun's, and for unexported fun's when it adds real value for documentation purposes. Define as many types as needed.
##### Record names
> Record names must use only lowercase characters. Words in record names must be separated with `_`. Same rule applies to record field names
*Examples*: [specs](src/specs.erl)
*Examples*: [record_names](src/record_names.erl)

*Reasoning*: Dialyzer output is complicated as is, and it is improved with good type names. In general, having semantically loaded type names for arguments makes reasoning about possible type failures easier, as well as the function's purpose.
*Reasoning*: Record and field names are atoms, they should follow the same rules that apply to them.

***
##### Records go first
> Records that are used within a module should be defined before any function bodies.
*Examples*: [record_placement](src/record_placement.erl)

*Reasoning*: Records are used to define data types that will most likely be used by multiple functions on the module, so their definition can not be tied to just one. Also, since records will be associated to types, it's a good practice to place them in code in a similar way as the documentation does (and edoc puts types at the beginning of each module documentation)

***
##### Don't share your records
> Records should not be shared among multiple modules. If you need to share _objects_ that are represented as records, use opaque exported types and provide adequate accesor functions in your module.
*Examples*: [record_sharing](src/record_sharing.erl)

*Reasoning*: Records are used for data structure definitions. Hiding those structures aids encapsulation and abstraction. If a record structure needs to be changed and it's definition is written in a .hrl file, the developer should find all the files where that .hrl and verify that his change hasn't broken anything. That's not needed if the record structure is internal to the module that manages it.

***
##### Avoid records in specs
Expand All @@ -313,6 +333,16 @@ Erlang syntax is horrible amirite? So you might as well make the best of it, rig

*Reasoning*: Types can be exported, which aids documentation and, using ``opaque`` types it also helps with encapsulation and abstraction.

### Misc

***
##### Write function specs
> Write the **-spec**'s for your exported fun's, and for unexported fun's when it adds real value for documentation purposes. Define as many types as needed.
*Examples*: [specs](src/specs.erl)

*Reasoning*: Dialyzer output is complicated as is, and it is improved with good type names. In general, having semantically loaded type names for arguments makes reasoning about possible type failures easier, as well as the function's purpose.

***
##### Use -callback attributes over behaviour_info/1.
> Unless you know your project will be compiled with R14 or lower, use ``-callback`` instead of ``behavior_info/1`` for your behavior definitions.
Expand Down
1 change: 1 addition & 0 deletions include/record_sharing.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-record(bad, {}).
11 changes: 11 additions & 0 deletions src/record_names.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-module(record_names).

-export([records/0]).

-record(badName, {}).
-record(bad_field_name, {badFieldName}).
-record('UPPERCASE', {'THIS_IS_BAD'}).

-record(good_name, {good_field_name}).

records() -> [#badName{}, #bad_field_name{}, #'UPPERCASE'{}, #good_name{}].
11 changes: 11 additions & 0 deletions src/record_placement.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-module(record_placement).

-export([good/0, bad/0]).

-record(good, {this, record, appears, before, the_functions}).

good() -> [#good{}].

-record(bad, {this, record, appears, below, a_function}).

bad() -> [#bad{}].
21 changes: 21 additions & 0 deletions src/record_sharing.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-module(record_sharing).

-include("record_sharing.hrl").

-export([bad/0, good/0, good_field/1, good_field/2]).

-record(good, {good_field :: string()}).
-opaque good() :: #good{}.
-export_type([good/0]).

-spec good() -> good().
good() -> #good{}.

-spec good_field(good()) -> string().
good_field(#good{} = Good) -> Good#good.good_field.

-spec good_field(good(), string()) -> good().
good_field(#good{} = Good, Value) -> Good#good{good_field = Value}.

-spec bad() -> #bad{}.
bad() -> #bad{}.

0 comments on commit 1fdd1ac

Please sign in to comment.