TypedStruct 0.2.0: plugin interface
TD;DR A new version of TypedStruct is available with a plugin interface and some bug fixes.
For nearly two years now, TypedStruct has helped me and other people in the Elixir community to define typed structs without writing boilerplate code. Its core functionality is fairly minimal and I aim to keep it as such.
Nonetheless, I have had some feature requests about integrating with Ecto or a lens library. While integrating with Ecto has been on my roadmap from the beginning, many projects using TypedStruct do not use Ecto. As every project has different needs, adding new features to TypedStruct would lead to controversial decisions. To conceal both the need to extend TypedStruct and keep it minimal, I went to think about a plugin system and started to effectively work on it a year ago.
Since then, I have been hired by the French Polar Institute to be system administrator—among other things—in Kerguelen Islands for the 2020 overwintering. This is a several-years-old project which has come to life, rearranging my priorities. I have been able to work on TypedStruct only sparsingly in August, then in November while at see on the Marion Dufresne in our way to Kerguelen. Here, my free time has been shared between community life and communicating with my family and friends, writing rather long descriptive emails. Hopefully I will publish a blog post about this extraordinary experience some day.
I had in mind a virtual deadline to get back on software development projets around April, when the summer campaign would end. I effectively started to crave software development at that time, then rearranged my priorities to get to it while keeping a good life balance.
So, after a chaotic year for me in the scope of software development, I am proud to annonuce TypedStruct 0.2.0 is live! Let me show you what this release contains.
Plugin interface
The biggest feature in TypedStruct 0.2.0 is the addition of a plugin interface.
As a user, that means you will be able to add new features to your typed
structs. For instance, to automatically generate lenses for your fields with the
Lens library, you can add
typed_struct_lens
to your Mix
dependencies and do:
defmodule MyStruct do
use TypedStruct
typedstruct do
# This line enables the plugin for the struct.
plugin TypedStructLens
field :a_field, String.t()
field :other_field, atom()
end
@spec change(t()) :: t()
def change(data) do
# a_field/0 is generated by TypedStructLens.
lens = a_field()
put_in(data, [lens], "Changed")
end
end
As a library developer, you can create plugins by implementing the
TypedStruct.Plugin
behaviour.
You can use three callbacks to inject code at different steps:
init/1
lets you inject code where theplugin/2
macro is called,field/3
lets you inject code on each field definition,after_definition/1
lets you insert code after the struct and its type have been defined.
The documentation features an example using all three callbacks so that you have an idea on how to proceed.
Already existing plugins
I have released two plugins concurrently to TypedStruct 0.2.0:
typed_struct_lens
integrates TypedStruct with the Lens library;typed_struct_legacy_reflection
re-enables the legacy reflection functions from TypedStruct 0.1.x.
I may work on an Ecto plugin at some point in the future, but feel free to start
something if you do not see a typed_struct_ecto
repository appearing on my
GitHub profile soon enough for you.
If you create a plugin, feel free to send me an email so that I am aware of it. I may also help you if I find it useful for my own use.
No more reflection
Since its very first version, TypedStruct had provided a simple way to access
information from the struct after the module had been compiled: three reflection
functions named __fields__/0
, __defaults__/0
and __typed__/0
were created
in each module.
With the new plugin interface, these functions are not strictly needed anymore, so I have removed them. To keep the compatibility with projects using it, I provide a plugin that defines these legacy functions. If you rely on these functions, please read the Updating section.
Bug fixes
During the development of TypedStruct 0.2.0, I came accross a tiny soundness
bug: the typedstruct
block was not scoped. This means the field
macro was
available oustide of the block, for instance. This is now resolved and the
typedstruct
block has a scope as expected.
Two other bugs where found by :
- fields with a default value set to
nil
were still enforced when it should not be the case (#14); - it was possible to clash with internal module attributes defined by
TypedStruct (#15).
These attributes are now prefixed by
ts_
and are deleted after the struct has been defined.
If you find other issues with TypedStruct or even design flaws, please open an issue.
Updating
To update from TypedStruct 0.1.x, simply replace the previous version in your Mix dependencies with:
{:typed_struct, "~> 0.2.0"}
If you are not using the reflection functions, you should be fine. Otherwise, you must also add to your Mix dependencies:
{:typed_struct_legacy_reflection, "~> 1.0.0"}
and in the structs where you need the reflection functions:
defmodule MyStruct
use TypedStruct
typedstruct do
# Add this line.
plugin TypedStructLegacyReflection
field :a_field, String.t()
field :another_field, atom()
end
end
Conclusion
I hope you will enjoy and make use of this plugin interface to integrate your own needs with TypedStruct. If the plugin API proves to be correctly designed, TypedStruct 0.2.0 is a good candidate to become 1.0.0 after some time.
As always, if you have some remarks or just want to add something, please send me an email.