From 1fdd1acf96b3bbae51fe70efd725f50d99843f46 Mon Sep 17 00:00:00 2001 From: Brujo Benavides Date: Tue, 16 Dec 2014 17:40:42 -0300 Subject: [PATCH] Assorted Record Rules --- Makefile | 2 +- README.md | 42 ++++++++++++++++++++++++++++++++------ include/record_sharing.hrl | 1 + src/record_names.erl | 11 ++++++++++ src/record_placement.erl | 11 ++++++++++ src/record_sharing.erl | 21 +++++++++++++++++++ 6 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 include/record_sharing.hrl create mode 100644 src/record_names.erl create mode 100644 src/record_placement.erl create mode 100644 src/record_sharing.erl diff --git a/Makefile b/Makefile index 244426f..dee6799 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,3 @@ PROJECT = erlang_standards -include erlang.mk \ No newline at end of file +include erlang.mk diff --git a/README.md b/README.md index f4e13f9..f7d8c9b 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 @@ -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. diff --git a/include/record_sharing.hrl b/include/record_sharing.hrl new file mode 100644 index 0000000..6f7f465 --- /dev/null +++ b/include/record_sharing.hrl @@ -0,0 +1 @@ +-record(bad, {}). diff --git a/src/record_names.erl b/src/record_names.erl new file mode 100644 index 0000000..16e9e9b --- /dev/null +++ b/src/record_names.erl @@ -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{}]. diff --git a/src/record_placement.erl b/src/record_placement.erl new file mode 100644 index 0000000..55bdef3 --- /dev/null +++ b/src/record_placement.erl @@ -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{}]. diff --git a/src/record_sharing.erl b/src/record_sharing.erl new file mode 100644 index 0000000..339a4af --- /dev/null +++ b/src/record_sharing.erl @@ -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{}.