![]()
I see. %A{} != %A{} is kind of ugly thing, I agree. I like the principle “a cure should not be worse than a disease”. I proposed a cure, but it has this side effect. Therefore, if we agree that disease is worse than having %A{} != %A{}, the proposal has a chance. So, let me show symptoms of “disease” more explicit.
My initial problem was about “Ecto cannot do dynamic defaults”. There is no proper workaround. autogenerate has pretty different behavior and does not work for virtual fields. I was thinking about proposing something like this:
defmodule Reminder do
use Ecto.Schema
schema "users" do
field :remind_at, :datetime, default_fn: &today_plus_10_days/0
# ... rest of the fields ...
end
end
Then I thought about implementation. Usually we create a record like this:
%Reminder{}
|> create_changeset()
Ecto also explicitly allows to do:
Repo.insert(%Reminder{})
Where this “default_fn” should be invoked? There is no guarantee that changeset will be used so we cannot put it into changeset. Patch all changeset functions and Repo functions to invoke “default_fns”? Even if we do this we have another problem: people do factories in the following way:
def reminder_factory(...) do
%Reminder{
user: build(:user)
}
end
How to make “default_fn” to work here?
If even creators of Ecto (and ExMachina) fell into the trap that locked them from providing reliable dynamic defaults without huge refactoring - it means we have a problem in the language design.
This is the disease I’m fighting with here. All the code in this message is idiomatic and comes from widely used libraries. It’s not about mere “struct creation with defaults”, it’s about preventing libraries from supporting dynamic defaults.
While I do not like introducing %A{} != %A{} I still see it as an acceptable tradeoff. Also, “blast radius” is controllable: you accept this tradeoff explicitly when prefer defdynstruct over defstruct.
The fact that default: DateTime.utc_today() is resolved at compile time actually also a surprise of a similar level. In my experience people constantly do this mistake. By using defdynstruct you replace one surprise with another, you do not increase overall amount of surprises (but I agree we increase variety).
Did I change how you see the problem? =)


















