DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Data Engineering
  3. Databases
  4. Erlang: records

Erlang: records

Giorgio Sironi user avatar by
Giorgio Sironi
·
Nov. 28, 12 · Interview
Like (0)
Save
Tweet
Share
3.93K Views

Join the DZone community and get the full member experience.

Join For Free

Erlang is mostly a functional language, and functions go hand in hand with immutable data structures that are passed around on the stack to represent the state of a process.

One of the simplest way to represent a record-like structure with a fixed number of fields is with a tuple. To create a Purchase tuple, we can link up the Name, Price and Merchant variables into a single structure:

Purchase = {Name, Price, Merchant}. 

Extracting the values again is performed via pattern matching on the tuple's structure:

doSomething({Name, Price, Merchant}) ->
  execute(Name). 

However, this structure is prone to break upon modifications. For example:

  • adding a date field means all the code pattern matching the tuple must be reviewed and updated to add another field.
  • The same goes if we remove merchant or another field, or if we want to reorder the fields for importance.
  • The structure does not scale to even very few fields (say 6), the ones you would put into a relational table.

Thus, for representing a C-like struct, Erlang provides a record type that you can use to specify a little abstraction over plain tuples.

Abstraction

A record's underlying representation is still a tuple, but it is accessed by name instead of by index. However, it can be compared more to a C struct or a Java object than to a map, since it is not dynamic: the name of fields and their number are fixed at compile time.

Records scale the tuple model to more fields, decoupling the accessed and modifications of fields from knowing anything about unrelated fields in the same record (save the record name).

There are runtime checks builtin when you pattern match on records, so while calling a function that expects a record with the wrong argument, you'll see a function_clause error indicating a bug.

Getting our hands dirty

There's an effective way of learning new syntaxes and models for coding: try them. So I set out with O'Reilly Erlang Programming book and wrote exploration tests for Erlang's own syntax that is explained there.
 
Defining a record is simple: you have to provide a name and a list of fields, which are all atoms. Additionally, you can specify default values for fields; in this case I'm using an atom too as a value, but it can be anything, even another record.

-module(records_10).
-include_lib("eunit/include/eunit.hrl").
-record(purchase, {name, price, merchant=default}).

Creating a record means assigning to a new variable that can contain the data structure. The syntax makes use of # to identify a record with the chosen atom.

record_creation_test() ->
  P = #purchase{name="Giorgio",
  price=10.00,
  merchant=bakery},
  ?assertEqual("Giorgio", P#purchase.name). 

Default values can of course be omitted:

record_default_value_test() ->
  P2 = #purchase{name="Giorgio",
  price=10.00},
  ?assertEqual(default, P2#purchase.merchant). 

Modifying a record only involves the fields whose value you want to change; since Erlang variables are immutable, you will have to assign the record to a new identifier.

record_modification_test() ->
  P = #purchase{name="Giorgio", price=10.00},
  NewP = P#purchase{price=20.00},
  ?assertEqual(#purchase{name="Giorgio", price=20.00},
  NewP). 

This should remind you of the classic Value Object pattern: immutable values that generate a new structure upon modification. However, behavior for these "objects" is left to functions accepting them as arguments, and there is no encapsulation of fields nor hiding of the internal structure (which for many Value Objects is really fine.)

Pattern matching can be performed not only on tuples and lists but also on records:

record_pattern_matching_test() ->
  P = #purchase{name="Giorgio", price=10.00},
  ?assertEqual(2.0, fixed_tax(P)). 
fixed_tax(#purchase{price=Price} = Purchase) ->
   Price * 0.20. 

Like for multiple function clauses, constraints can be specified in the patterns in order to select the appropriate body:

record_multiple_pattern_matching_test() ->
  ?assertEqual(2.0, variable_tax(#purchase{name="Giorgio",
  price=10.00})),
  ?assertEqual(0.4, variable_tax(#purchase{name="Giorgio",  
  price=10.00,
  merchant=bakery})). 
variable_tax(#purchase{price=Price,merchant=bakery} = Purchase) ->
   Price * 0.04;
variable_tax(#purchase{price=Price} = Purchase) ->
   Price * 0.20. 

Finally, you can also introspect a bit how a record structure contains, with a reflection function:

record_info_on_fields_test() ->
  ?assertEqual([name, price, merchant],
  record_info(fields, purchase)). 

Conclusions

Programming is not only building a Turing-complete platform but also satisfying many non functional requirements like the ability to evolve the code in months and years. Defining records instead of tuples helps in maintenance and in expressing concepts which could remain hidden in the variable soup.

All the code for this article is available on Github.

Database Erlang (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Real-Time Analytics for IoT
  • 5 Software Developer Competencies: How To Recognize a Good Programmer
  • Front-End Troubleshooting Using OpenTelemetry
  • DevOps for Developers: Continuous Integration, GitHub Actions, and Sonar Cloud

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: