Skip to content

Discussion: refactor as pure AST -> AST transformation #37

@bamorim

Description

@bamorim

After the problems we were having with #34 I started thinking if we should change how we generate the types.

Current Approach

The current approach is mostly an "imperative" solution.

typed_schema and typed_embedded_schema macros get the AST and transforms it so that field, timestamps, embeds_one, embeds_many, belongs_to, has_many, has_one and many_to_many translate to something like this

field(:name, :string)
TypeBuilder.add_field(__MODULE__, :field, :name, :string, [])

Then the TypeBuilder.add_field will store type metadata in the module attribute so later TypeBuilder.define_type macro will generate a @type definition.

Proposed Approach

My idea would be to make Typed Ecto Schema more of a simpler AST to AST translation which would generate a AST more similar to how one would write things manually. So that would mean having a pure function that generates the final AST with no calls to any TypedEctoSchema module.

We would even be able to make more isolated tests like

test "generates typecheck code" do
  code = quote do
    field(:str, :string)
  end
  
  type = quote do
    %__MODULE__{
      __meta__:  Ecto.Metadata.t(),
      str: String.t() | nil
    }
  end
  
  assert_equivalent TypeGenerator.type_for(code, :schema, [primary_key: false]), type
end

Another benefit is that this would allow one to "eject" out of TypedEctoSchema if they want to later on by translating the calls on the file (that would require extra tooling, but the core logic would be there).

But mainly, I think after the recursive tree-traversal code is defined, it will be way easier to understand the code as there is less state.

For enforced keys, we would do another traversal getting the enforce: options in the fields completely separated.

I'm still not sure how complicated that is, but I'm starting this discussion to think if this is something worth going for and how we can go about it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions