sunaku
Hey folks,
I’m pleased to announce StructyRecord, my very first Elixir library! ![]()
- GitHub - GitHub - sunaku/structy_record: 🎩 Provides a Struct-like interface for Records · GitHub
- Hex.pm - structy_record | Hex
I created this library to improve the usability of Records (which are clunky to use in comparison to the first-class language support for Structs) because my performance-sensitive Elixir applications make heavy use of ETS tables that rely on Records for data representation.
I hope that it’s also useful to you, enjoy! ![]()
Cheers.
Readme
StructyRecord provides a Struct-like interface for your Records.
- Use your record’s macros in the same module where it is defined!
- Access and update fields in your record through named macro calls.
- Create and update records at runtime (not limited to compile time).
- Calculate 1-based indexes to access record fields in
:etstables.
To get started, see the documentation for StructyRecord.defrecord/3:
iex> h StructyRecord.defrecord
Features
The defined module provides the following guards, macros, and functions.
Guards:
is_record/1to check if argument loosely matches this record’s shape
Macros:
record?/1to check if argument strictly matches this record’s shaperecord/0to create a new record with default values for all fieldsrecord/1to create a new record with the given fields and valuesrecord/1to get the zero-based index of the given field in a recordrecord/1to convert the given record into aKeywordlistrecord/2to get the value of a given field in a given recordrecord/2to update an existing record with the given fields and values${field}/1to get the value of a specific field in a given record${field}/2to set the value of a specific field in a given recordkeypos/1to get the 1-based index of the given field in a record
Functions:
record!/1to create a new record at runtime with the given fields and valuesrecord!/2to update an existing record with the given fields and values
Examples
Activate this macro in your environment:
require StructyRecord
Define a structy record for a rectangle:
StructyRecord.defrecord Rectangle, [:width, :height] do
def area(r=record()) do
width(r) * height(r)
end
def perimeter(record(width: w, height: h)) do
2 * (w + h)
end
def square?(record(width: same, height: same)), do: true
def square?(_), do: false
end
Activate its macros in your environment:
use Rectangle
Create instances of your structy record:
rect = Rectangle.record() #-> {Rectangle, nil, nil}
no_h = Rectangle.record(width: 1) #-> {Rectangle, 1, nil}
no_w = Rectangle.record(height: 2) #-> {Rectangle, nil, 2}
wide = Rectangle.record(width: 10, height: 5) #-> {Rectangle, 10, 5}
tall = Rectangle.record(width: 4, height: 25) #-> {Rectangle, 4, 25}
even = Rectangle.record(width: 10, height: 10) #-> {Rectangle, 10, 10}
Get values of fields in those instances:
tall |> Rectangle.height() #-> 25
tall |> Rectangle.record(:height) #-> 25
Rectangle.record(height: h) = tall; h #-> 25
Set values of fields in those instances:
even |> Rectangle.width(1) #-> {Rectangle, 1, 10}
even |> Rectangle.record(width: 1) #-> {Rectangle, 1, 10}
even |> Rectangle.width(1) |> Rectangle.height(2) #-> {Rectangle, 1, 2}
even |> Rectangle.record(width: 1, height: 2) #-> {Rectangle, 1, 2}
Use your custom code on those instances:
rect |> Rectangle.area() #-> (ArithmeticError) bad argument in arithmetic expression: nil * nil
no_h |> Rectangle.area() #-> (ArithmeticError) bad argument in arithmetic expression: 1 * nil
no_w |> Rectangle.area() #-> (ArithmeticError) bad argument in arithmetic expression: nil * 2
wide |> Rectangle.area() #-> 50
tall |> Rectangle.area() #-> 100
even |> Rectangle.area() #-> 100
rect |> Rectangle.perimeter() #-> (ArithmeticError) bad argument in arithmetic expression: nil + nil
no_h |> Rectangle.perimeter() #-> (ArithmeticError) bad argument in arithmetic expression: 1 + nil
no_w |> Rectangle.perimeter() #-> (ArithmeticError) bad argument in arithmetic expression: nil + 2
wide |> Rectangle.perimeter() #-> 30
tall |> Rectangle.perimeter() #-> 58
even |> Rectangle.perimeter() #-> 40
rect |> Rectangle.square?() #-> true
no_h |> Rectangle.square?() #-> false
no_w |> Rectangle.square?() #-> false
wide |> Rectangle.square?() #-> false
tall |> Rectangle.square?() #-> false
even |> Rectangle.square?() #-> true
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #genstage
- #ai
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #performance










First 5 of 5 Posts
OvermindDL1
Now if only the Elixir built-in Protocol’s supported dispatch based on the first tuple element. ^.^;
Love records though!
sunaku
Version 0.2.0 (2021-02-18)
This release adds a convenient new shorthand syntax for the
record()macro,renames field accessors to prevent name collisions, clarifies docs, and more.
Incompatible:
Rename
record!/1function tofrom_list/1.Rename
record!/2function tomerge/2.Add
get_andput_prefix to field accessors.Field names can no longer conflict with defined macros & functions.
Don’t check argument types in Elixiry interface.
It broke simple macro expansion when used in case/function clauses.
Enhancements:
Add
Module.{_}syntax forModule.record(_).https://stackoverflow.com/a/51313720/120075
Add
inspect/2for friendlier inspection.Add
to_list/0to get record’s template.Add
to_list/1alias forrecord/1macro.Add
index/1to get field index in tuple.Add
get/2macro as an alias torecord/2.Add
put/2macro as an alias torecord/2.Define documentation for all macros and functions.
Housekeeping:
record?/1macro: only use pattern matching check.keypos/1macro: don’t call module being defined.mix.exs: drop application(); use
runtime: false.mudasobwa
You might want to check this blog for the insight of how to handle it differently in guards.
sunaku
Great article! That unravels the mystery behind how records magically work in pattern matches and behave like normal functions otherwise. I’m eager to apply this knowledge in future releases. Thanks.
sunaku
Version 0.2.1 (2021-02-20)
Housekeeping:
README:
runtime: falsesuggestion broke releases.The
runtime: falseflag in the user’smix.exsfile prevents thestructy_recordapplication (which bundles moduleStructyRecord)from being included in app releases. This breaks defined macros and
functions that delegate to the
StructyRecord.from_list/3functionbecause the
StructyRecordmodule won’t be included in app releases!