The compile-time problem and the current fix led me to think our current type inference is not ideal.
We treat assocs (has_many, has_one, belongs_to, many_to_many) and embeds (embeds_one, embeds_many) the same way we treat field.
The problem is that some assumptions don't translate very well.
For example, while for assocs it always have a schema module being defined, fields can be primitives, custom types, composite types or parametrized types.
The problem with custom types (parametrized or not) is that the type name is a module and our current inference then defaults to mod.t() as the type.
This is a good one for assocs since, I assume, most projects using typed_ecto_schema would be using typed_schema/typed_embedded_schema for all schemas, which would always generate a t() type.
That however, is not good for ecto types because it won't be using our library to implement Ecto.Type behaviour.
This is why we have weird edge cases for things like the Ecto.Enum.
My idea going forward would be to split the type inference of assocs/embeds and fields completely.
With that I'd actually re-introduce compile-time dependency for field, so we can check a custom method like beam_type/1 that could be added to Ecto.Types to generate the beam type. We could even have a way to config modules defining these types for externally defined ecto.types, which could even be how we define the type for Ecto.Enum.
Also, if we couldn't infer, I think the default should be any() instead of mod.t()
Then, for assocs, we could leave the way it currently is (defaulting to mod.t()).
The compile-time problem and the current fix led me to think our current type inference is not ideal.
We treat assocs (
has_many
,has_one
,belongs_to
,many_to_many
) and embeds (embeds_one
,embeds_many
) the same way we treatfield
.The problem is that some assumptions don't translate very well. For example, while for assocs it always have a schema module being defined, fields can be primitives, custom types, composite types or parametrized types.
The problem with custom types (parametrized or not) is that the type name is a module and our current inference then defaults to
mod.t()
as the type.This is a good one for assocs since, I assume, most projects using typed_ecto_schema would be using
typed_schema
/typed_embedded_schema
for all schemas, which would always generate at()
type.That however, is not good for ecto types because it won't be using our library to implement
Ecto.Type
behaviour.This is why we have weird edge cases for things like the Ecto.Enum.
My idea going forward would be to split the type inference of assocs/embeds and fields completely.
With that I'd actually re-introduce compile-time dependency for field, so we can check a custom method like
beam_type/1
that could be added toEcto.Type
s to generate the beam type. We could even have a way to config modules defining these types for externally defined ecto.types, which could even be how we define the type forEcto.Enum
.Also, if we couldn't infer, I think the default should be
any()
instead ofmod.t()
Then, for assocs, we could leave the way it currently is (defaulting to
mod.t()
).Thoughts on that?