Hey folks,
I’m pleased to announce StructyRecord, my very first Elixir library!
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 Record
s.
- 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
:ets
tables.
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/1
to check if argument loosely matches this record’s shape
Macros:
-
record?/1
to check if argument strictly matches this record’s shape -
record/0
to create a new record with default values for all fields -
record/1
to create a new record with the given fields and values -
record/1
to get the zero-based index of the given field in a record -
record/1
to convert the given record into aKeyword
list -
record/2
to get the value of a given field in a given record -
record/2
to update an existing record with the given fields and values -
${field}/1
to get the value of a specific field in a given record -
${field}/2
to set the value of a specific field in a given record -
keypos/1
to get the 1-based index of the given field in a record
Functions:
-
record!/1
to create a new record at runtime with the given fields and values -
record!/2
to 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