kanishka
Is there an equivalent to a "private constructor" for elixir structs?
Are there patterns similar to having a private constructor along with a public build/validate function to guide users of a struct module to build valid instances? I understand that you may not be able to completely stop a user from creating a struct with invalid field values, but just wonder if there are patterns for something along those lines. I understand that you can use Ecto validations with embedded schemas to express some constraints, but wondering if there some pattern for plain structs
Marked As Solved
sodapopcan
There is not. A common pattern is to define a new/1 for this and you just have to document that that is what should be used.
Edit: sorry “there is not” referring to not being able to mail a struct as private.
Also Liked
sabiwara
In addition to other answers, if you are using typespecs and dialyzer it might be interesting to add a definition of an opaque type for your struct to achieve some kind of encapsulation.
defmodule MyStruct do
@opaque t :: %__MODULE__{foo: integer()}
@enforce_keys [:foo]
defstruct [:foo]
# functions in this module can create or manipulate internals of t()
@spec new(integer()) :: t()
def new(foo) when is_integer(foo), do: %__MODULE__{foo: foo}
end
defmodule OtherModule do
# dialyzer will complain since you can't assume the internals of MyStruct.t() outside of MyStruct
@spec create_struct() :: MyStruct.t()
def create_struct() do
%MyStruct{foo: 0}
end
end
Dialyzer will report:
other_module.ex:3:contract_with_opaque
The @spec for OtherModule.create_struct/0 has an opaque
subtype MyStruct.t() which is violated by the success typing.
While this doesn’t offer strong guarantees, it can help detect cases that are by-passing your constructor in your codebase.
gregvaughn
A few years back I used Ecto with embedded schemas to act as an anti-corruption layer at the edge of our system to validate incoming in-memory data from third parties into known structs for our core business logic. I ended up writing about a 20-25 line module with a __using__ macro that streamlined it for the codebase. I prefer that over adding a dependency.
tme_317
After trying several community options I’ve been using @bitwalker’s simple Strukt library for a few months and really like it. It is very similar to using embedded schemas and validations but without much of the boilerplate. It defines new and change functions in the module with several methods of validation compatible with Ecto changesets.
Popular in Questions
Other popular 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
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








